package com.cmc.cloud.cmclink.doc.service.impl.si;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.bdt.api.base.FinanceItemApi;
import com.cmc.cloud.cmclink.bdt.api.base.dto.req.FinanceItemListByCodeReqVO;
import com.cmc.cloud.cmclink.bdt.api.base.dto.resp.FinanceItemRespDTO;
import com.cmc.cloud.cmclink.csm.api.base.dto.req.CompanyNameCastReqVO;
import com.cmc.cloud.cmclink.csm.api.base.dto.resp.CompanyNameCastRespDTO;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.api.document.rep.BlnoCntrStaticDto;
import com.cmc.cloud.cmclink.doc.api.document.rep.BlnoPageRespDto;
import com.cmc.cloud.cmclink.doc.api.document.rep.BlnoStatisticsRespDto;
import com.cmc.cloud.cmclink.doc.api.document.req.si.*;
import com.cmc.cloud.cmclink.doc.api.document.req.so.SoForecastBaseVO;
import com.cmc.cloud.cmclink.doc.api.document.req.so.SoForecastRespVO;
import com.cmc.cloud.cmclink.doc.configuration.CommonConfiguration;
import com.cmc.cloud.cmclink.doc.constants.*;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.*;
import com.cmc.cloud.cmclink.doc.convert.BlnoConvert;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.enums.PrintStatusEnum;
import com.cmc.cloud.cmclink.doc.enums.filter.SiFilterConditionFieldEnum;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.CustomerRpcService;
import com.cmc.cloud.cmclink.doc.rpcservice.DictDataService;
import com.cmc.cloud.cmclink.doc.rpcservice.LaneService;
import com.cmc.cloud.cmclink.doc.rpcservice.VoyScheduleService;
import com.cmc.cloud.cmclink.doc.rpcservice.vo.LaneRespVO;
import com.cmc.cloud.cmclink.doc.service.BlnoCntrService;
import com.cmc.cloud.cmclink.doc.service.BlnoSendEdoService;
import com.cmc.cloud.cmclink.doc.service.LimitRuleService;
import com.cmc.cloud.cmclink.doc.service.SpecialCargoDgService;
import com.cmc.cloud.cmclink.doc.service.VgmService;
import com.cmc.cloud.cmclink.doc.service.si.*;
import com.cmc.cloud.cmclink.doc.service.so.*;
import com.cmc.cloud.cmclink.doc.util.CheckUtil;
import com.cmc.cloud.cmclink.doc.util.CostChargeUtil;
import com.cmc.cloud.cmclink.doc.util.HttpClientUtil;
import com.cmc.cloud.cmclink.doc.util.I18nsUtil;
import com.cmc.cloud.cmclink.doc.util.VslvoyUtil;
import com.cmc.cloud.cmclink.doc.util.respvo.CommodityEnAiCheckResp;
import com.cmc.cloud.cmclink.doc.util.so.CmclinkExpressionUtil;
import com.cmc.cloud.cmclink.doc.vo.blnoblprintlogvo.BlnoPrintBatchOperationReqDto;
import com.cmc.cloud.cmclink.doc.vo.blnocargovo.BlnoCargoUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnochargevo.BlnoCheckMsgReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnocntrvo.BlnoCntrUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnocntrvo.CntrBaseInfoVO;
import com.cmc.cloud.cmclink.doc.vo.blnofreightvo.BlnoFreightCalReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnofreightvo.BlnoFreightUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnolocalcostvo.BlnoLocaCoastExRespVO;
import com.cmc.cloud.cmclink.doc.vo.blnolocalcostvo.BlnoLocalCostExReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnopaymentvo.BlnoPaymentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnoscnvo.BlnoScnSameVO;
import com.cmc.cloud.cmclink.doc.vo.blnoscnvo.BlnoScnUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.*;
import com.cmc.cloud.cmclink.doc.vo.blnovo.operatevo.*;
import com.cmc.cloud.cmclink.doc.vo.blnovo.sichangevo.*;
import com.cmc.cloud.cmclink.doc.vo.blnovslvoyvo.BlnoVslvoySameVO;
import com.cmc.cloud.cmclink.doc.vo.blnovslvoyvo.BlnoVslvoyUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.limitrulevo.LimitRulePageReqVO;
import com.cmc.cloud.cmclink.doc.vo.limitrulevo.LimitRuleRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.operatelog.OperateLogCreateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.operatelog.OperateLogRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.requirementsvo.RequirementsRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.requirementsvo.RequirementsUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sobcrcheck.SoBcrCheckCreateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sobcrcheck.SoBcrCheckRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovslvoyvo.SoVslvoyScheduleVO;
import com.cmc.cloud.cmclink.doc.vo.specialcargodgvo.SuspectedDgReqVO;
import com.cmc.cloud.cmclink.doc.vo.vgm.VgmCreateReqVO;
import com.cmc.cloud.cmclink.doc.vo.voyagelock.VoyageLockImReqVO;
import com.cmc.cloud.cmclink.eqm.api.model.request.ImReleaseStatusReqParam;
import com.cmc.cloud.cmclink.eqm.api.model.response.CntrBaseInfoDTO;
import com.cmc.cloud.cmclink.eqm.api.model.response.ImReleaseStatusDTO;
import com.cmc.cloud.cmclink.eqm.api.resource.CntrInfoApi;
import com.cmc.cloud.cmclink.eqm.api.usage.EirReleaseApi;
import com.cmc.cloud.cmclink.svl.api.base.req.BargePolPodReqDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmc.cloud.cmclink.system.api.api.dict.DictDataApi;
import com.cmc.cloud.cmclink.system.api.api.dict.dto.DictDataRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.CommonResult;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.ex.Assertion;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import com.cmes.platform.system.nuc.NucApi;
import com.cmes.platform.system.nuc.dto.UserProfileDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.cmc.cloud.cmclink.doc.api.document.emums.BlnoCheckSiErrorPromptEnum.*;
import static com.cmc.cloud.cmclink.doc.api.document.emums.CntaoCntrCompareTypeEnum.CNTR_TYPE_SIZE_DIFFERENT;
import static com.cmc.cloud.cmclink.doc.api.document.emums.CntaoCntrCompareTypeEnum.POD_DIFFERENT;
import static com.cmc.cloud.cmclink.doc.api.document.emums.ConfirmNoticeEnum.*;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SiChangeStatusEnum.SI_AMENDMENT_PROCESSING;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SiOperateTypeEnum.*;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SiSplitCombineEnum.*;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SiStatusEnum.BookingRejected;
import static com.cmc.cloud.cmclink.doc.constants.BlnoConstants.DOC_IRAN_SANCTIONS_RELATED_CNTR_PREFIX;
import static com.cmc.cloud.cmclink.doc.constants.LocalChargeConstants.CNTXG;

/**
 * 提单主信息 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
@Slf4j
public class BlnoServiceImpl implements BlnoService {

    private final static int CNTR_LENGTH = 11;
    private final static String SI_OPERATE_TYPE = "SI_Operate";
    private final static String CNTR_TYPE_RF = "RF";
    private final static String CNTR_TYPE_RH = "RH";
    private final static String CNTR_TYPE_OT = "OT";
    private final static String CNTR_TYPE_FR = "FR";
    private final static String CNTR_TYPE_HC = "HC";
    private final static List<String> BASIC_OPERTATE_TYPES = Arrays.asList(CONFIRM_SI.getValue(), SUBMIT_SI.getValue());

    public final static  List<String> SEND_EDO_PORT = Arrays.asList("CNTAO","CNTXG","CNDLC","CNRZH");
    public static final String CNSHA = "CNSHA";
    public static final String CNNGB = "CNNGB";
    @Resource
    private CommonConfiguration configuration;
    /**
     * 越南港前缀
     */
    private final static String YUE_NAN = "VN";
    /**
     * 台湾港前缀
     */
    private final static String TAI_WAN = "TW";
    /**
     * 泰国港前缀
     */
    private final static String TAI_GUO = "TH";
    /**
     * 印度港前缀
     */
    private final static String INDIA_PREFEX = "IN";
    /**
     * check si 收费编码
     */
    private final static String CHARGE_CODE = "FRT";
    private static final String RULE_TYPE_PROHIBITED = "PROHIBITED";
    private static final String RULE_TYPE_SUPPLEMENTARY_MATERIALS = "SUPPLEMENTARY_MATERIALS";
    private static final String RULE_TYPE_INFORMATION_CONFIRM = "INFORMATION_CONFIRM";
    private static final String VGM = "VGM";

    /**
     * D/O
     */
    private static final String DO = "DO";
    /**
     * 交货记录  DELIVERY NOTE
     */
    private static final String DN = "DN";
    /**
     * ArrivalNotice
     */
    public static final String AN = "AN";
    private static final String DATA_TYPE = "ETU";
    @Resource
    private BlnoMapper blnoMapper;
    @Resource
    private OperateLogService operateLogService;
    @Resource
    private BlnoVslvoyService blnoVslvoyService;
    @Resource
    private BlnoPaymentService blnoPaymentService;
    @Resource
    private AttachmentService attachmentService;
    @Resource
    private RequirementsService requirementsService;
    @Resource
    private BlnoScnService blnoScnService;
    @Resource
    private BlnoCargoService blnoCargoService;
    @Resource
    private BlnoCntrService blnoCntrService;
    @Resource
    private CustomerRpcService nameTransferService;
    @Resource
    private BlnoFreightMapper blnoFreightMapper;
    @Resource
    private BlnoLocalChargeService blnoLocalChargeService;
    @Resource
    private BlnoLocalCostExService blnoLocalCostExService;
    @Resource
    private FinanceItemApi financeItemApi;
    @Resource
    private LimitRuleService limitRuleService;
    @Resource
    private SoBcrCheckService soBcrCheckService;
    @Resource
    private VgmService vgmService;
    @Resource
    private BlnoCntrMapper blnoCntrMapper;
    @Resource
    private BlnoVslvoyMapper blnoVslvoyMapper;
    @Resource
    private BlnoScnMapper blnoScnMapper;
    @Resource
    private BlnoPaymentMapper blnoPaymentMapper;
    @Resource
    private OperateLogMapper operateLogMapper;
    @Resource
    private BlnoBlPrintLogService blnoBlPrintLogService;
    @Resource
    private CntrInfoApi cntrInfoApi;
    @Resource
    private SoCargoMapper soCargoMapper;
    @Resource
    private SoForecastMapper soForecastMapper;
    @Resource
    private ManifestCargoMapper manifestCargoMapper;
    @Resource
    private SpecialCargoDgService specialCargoDgService;

    @Resource
    private SoMapper soMapper;
    @Resource
    private VoyageLockService voyageLockService;
    @Resource
    private BasicConfigMapper basicConfigMapper;
    @Resource
    private BlnoBlPrintLogMapper blnoBlPrintLogMapper;

    @Resource
    private BlnoCargoMapper blnoCargoMapper;

    @Resource
    private SoAmendApplyMapper soAmendApplyMapper;
    @Resource
    private BlnoMultipleSoService blnoMultipleSoService;
    @Resource
    private BlnoMultipleSoMapper blnoMultipleSoMapper;
    @Resource
    private BlnoSpiltCombineDetailMapper blnoSpiltCombineDetailMapper;
    @Resource
    @Lazy
    private SoSplitCombineService soSplitCombineService;

    @Resource
    ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private ManifestMapper manifestMapper;
    @Resource
    private DoInfoMapper doInfoMapper;
    @Resource
    private ConfirmNoticeService confirmNoticeService;

    @Resource
    private BlnoLocalChargeMapper blnoLocalChargeMapper;
    @Resource
    private ConfirmNoticeMapper confirmNoticeMapper;

    @Resource
    private BlnoSendEdoService blnoSendEdoService;
    @Resource
    private CostChargeUtil costChargeUtil;
    @Resource
    private NucApi nucApi;
    @Resource
    private VoyScheduleService voyScheduleService;
    @Resource
    private LaneService laneService;
    @Resource
    private I18nsUtil i18nsUtil;
    @Resource
    private DictDataService dictDataService;
    /**
     * 提单信息工具类
     */
    @Resource
    private VslvoyUtil vslvoyUtil;
    @Resource
    private EirReleaseApi eirReleaseApi;

    @Override
    public void createBlno(BlnoCreateReqVO createReqVO) {
        // 插入
        BlnoDO blno = BlnoConvert.INSTANCE.convert(createReqVO);
        blnoMapper.insert(blno);
    }

    @Override
    public void updateBlno(BlnoUpdateReqVO updateReqVO) {
        // 校验存在
        validateBlnoExists(updateReqVO.getId());
        // 更新
        BlnoDO updateObj = BlnoConvert.INSTANCE.convert(updateReqVO);
        blnoMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBlno(Long id) {
        // 校验存在
        BlnoDO blnoDO = blnoMapper.selectById(id);
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        if (!SiStatusEnum.BookingDraft.getValue().equals(blnoDO.getSiStatus())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NOT_DELETE);
        }
        if (!StringUtils.equals(blnoDO.getCreator(), String.valueOf(SecurityFrameworkUtils.getLoginUserId()))) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_USER_NOT_DELETE);
        }
        // 如果 该提单由多SO 合并而来 删除绑定关系 并且回滚原SO的提单号
        List<BlnoMultipleSoDO> blnoMultipleSoDos = blnoMultipleSoMapper.selectList(new LambdaQueryWrapper<BlnoMultipleSoDO>().eq(BlnoMultipleSoDO::getBlnoId, blnoDO.getId()));
        if (CollectionUtil.isNotEmpty(blnoMultipleSoDos)) {
            Map<Long, String> soMap = blnoMultipleSoDos.stream().collect(Collectors.toMap(BlnoMultipleSoDO::getSoId, BlnoMultipleSoDO::getSoBlNo));
            List<SoDO> soDos = soMapper.selectList(new LambdaQueryWrapper<SoDO>().in(SoDO::getId, soMap.keySet()));
            soDos.forEach(n -> {
                n.setBlNo(soMap.getOrDefault(n.getId(), n.getBlNo()));
            });
            soMapper.updateBatch(soDos);
            blnoMultipleSoMapper.delete(new LambdaQueryWrapper<BlnoMultipleSoDO>().eq(BlnoMultipleSoDO::getBlnoId, blnoDO.getId()));
        }
        // 删除
        blnoMapper.deleteById(id);
    }

    private void validateBlnoExists(Long id) {
        if (blnoMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
    }

    @Override
    public BlnoDO getBlno(Long id) {
        return blnoMapper.selectById(id);
    }

    @Override
    public List<BlnoDO> getBlnoList(Collection<Long> ids) {
        return blnoMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<BlnoPageRespVO> getBlnoPage(BlnoPageReqVO reqVO) {
        IPage<BlnoPageRespVO> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<BlnoPageRespVO> pageResult = blnoMapper.getBlnoBaseInfo(page, reqVO);
        List<BlnoPageRespVO> voList = BeanUtil.copyToList(pageResult.getRecords(), BlnoPageRespVO.class);
        if (CollectionUtil.isNotEmpty(voList)) {
            nameTransferService.fillName(voList);
        }
        return new PageResult<>(voList, pageResult.getTotal());
    }

    @Override
    public PageResult<BlnoImPageRespVO> getBlnoImPage(BlnoImPageReqVO reqVO) {
        IPage<BlnoPageRespVO> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<BlnoImPageRespVO> pageResult = blnoMapper.getBlnoIm(page, reqVO, getKeyLst(reqVO));
        List<BlnoImPageRespVO> voList = pageResult.getRecords();
        if (CollectionUtil.isNotEmpty(voList)) {
            voList.forEach(vo -> {
                //人民币已收外币已收	调用接口从财务获取，需配置数据字典
                vo.setPaid("PaidEnum.PAID.getValue()");
            });
        }
        return new PageResult<>(voList, pageResult.getTotal());
    }

    @Override
    public List<BlnoImPageRespVO> exportBlnoIm(BlnoImPageReqVO reqVO) {
        List<BlnoImPageRespVO> voList = blnoMapper.getBlnoIm(reqVO, getKeyLst(reqVO));
        if (CollectionUtil.isNotEmpty(voList)) {
            voList.forEach(vo -> {
                //人民币已收外币已收	调用接口从财务获取，需配置数据字典
                vo.setPaid("PaidEnum.PAID.getValue()");
            });
        }
        return voList;
    }

    public List<String> getKeyLst(BlnoImPageReqVO reqVO) {
        List<String> keyLst = new ArrayList<>();
        if (reqVO.getSelectTransit() != null && reqVO.getSelectTransit() && Objects.equals(reqVO.getCurrentPort(), CNTXG)) {

            keyLst = basicConfigMapper.selectList(new LambdaQueryWrapper<BasicConfigDO>()
                    .eq(BasicConfigDO::getCodeDefine, "过境信息")
                    .eq(BasicConfigDO::getPortCd, CNTXG)
                    .eq(BasicConfigDO::getEnable, true)
            ).stream().map(BasicConfigDO::getCode).collect(Collectors.toList());
        }
        return keyLst;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void returnDraft(Long id) {
        BlnoDO blnoDO = blnoMapper.selectById(id);
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        if (!SiStatusEnum.BookingProcessing.getValue().equals(blnoDO.getSiStatus())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_INFO_RETURN_ERROR);
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.eq(BlnoDO::getId, blnoDO.getId()).set(BlnoDO::getSiStatus,
                SiStatusEnum.BookingDraft.getValue());
        blnoMapper.update(null, lambdaUpdate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Lock4j(name = "siUnique", keys = {"'createOrUpdateSi'", "#userId"})
    public Long createOrUpdateSi(BlnoInfoVO createReqVO, boolean update, String userId) {
        BlnoInfoVO oldSi = new BlnoInfoVO();
        BlnoBaseInfoVO blnoBaseInfoVO = createReqVO.getBlnoBasInfoVO();
        if (CollectionUtil.isEmpty(createReqVO.getBlnoVslvoys())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_VOYAGE_NOT_HAVE);
        }
        if (update) {
            // 只有状态是【草稿】的 SI 才能编辑 SI。
            if (blnoBaseInfoVO != null && blnoBaseInfoVO.getId() != null) {
                oldSi = this.getDetail(blnoBaseInfoVO.getId());
//                checkUserLimit(oldSi.getBlnoBasInfoVO().getCreator(), userId);
            } else {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
            }
            if (!SoDataSourceEnum.EDI.getValue().equals(blnoBaseInfoVO.getDatasource()) && !StringUtils.equals(blnoBaseInfoVO.getSiStatus(), SiStatusEnum.BookingDraft.getValue())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NI_UPDATE);
            }
            if (createReqVO.getBlnoVslvoys().stream().anyMatch(n -> ObjectUtil.isNull(n.getId()) || ObjectUtil.isNull(n.getId()))) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_VOYAGE_NOT_HAVE);
            }
        } else {
            if (blnoBaseInfoVO != null && blnoBaseInfoVO.getId() != null) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_HAS_EXISTS);
            }
        }
        if(!SoDataSourceEnum.EDI.getValue().equals(blnoBaseInfoVO.getDatasource())){
            checkSiInfo(createReqVO);
        }
        BlnoDO blnoDO = this.createOrUpdateSi(blnoBaseInfoVO);
        List<BlnoCntrUpdateReqVO> cntrUpdateReqListList = createReqVO.getBlnoCntrs();
        if (CollectionUtil.isNotEmpty(cntrUpdateReqListList)) {
            createReqVO.getBlnoCntrs().forEach(n -> {
                if (StringUtils.isEmpty(n.getCntrOwner())) {
                    n.setCntrOwner(blnoBaseInfoVO.getCntrOwner());
                }
            });
        }
        attachmentService.createOrUpdateAndDeleteBatch(blnoDO.getId(), SoSiEnum.SI.getValue(),
                createReqVO.getAttachmentReqVO(), BeanUtil.copyToList(oldSi.getAttachmentReqVO(), AttachmentRespVO.class));
        requirementsService.createOrUpdateAndDeleteBatch(blnoDO.getId(), SoSiEnum.SI.getValue(),
                createReqVO.getReqVOList(), BeanUtil.copyToList(oldSi.getReqVOList(), RequirementsRespVO.class));
        blnoVslvoyService.createOrUpdateAndDeleteBatch(blnoDO.getId(), createReqVO.getBlnoVslvoys(),
                oldSi.getBlnoVslvoys());
        blnoPaymentService.createOrUpdateAndDeleteBatch(blnoDO.getId(), createReqVO.getBlnoPayments(),
                oldSi.getBlnoPayments());
        blnoScnService.createOrUpdateAndDeleteBatch(blnoDO.getId(), createReqVO.getBlnoScns(), oldSi.getBlnoScns());
        blnoCargoService.createOrUpdateAndDeleteBatch(blnoDO.getId(), createReqVO.getBlnoCargos(),
                oldSi.getBlnoCargos());
        blnoCntrService.createOrUpdateAndDeleteBatch(blnoDO.getId(), createReqVO.getBlnoCntrs(), oldSi.getBlnoCntrs());
        //vgm信息保存修改
        List<VgmCreateReqVO> vgmInfoList = createReqVO.getVgmCreateReqVos();
        if (CollectionUtil.isNotEmpty(vgmInfoList)) {
            vgmService.saveOrupdateVgmInfo(vgmInfoList);
        }
        return blnoDO.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long commitSi(BlnoInfoVO blnoInfoVO, String userId) {
        checkSiCommitInfo(blnoInfoVO);
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        Long orUpdateSi = 0L;
        if (ObjectUtil.isNull(blnoBasInfoVO.getId())) {
            orUpdateSi = this.createOrUpdateSi(blnoInfoVO, false, userId);
        } else {
            orUpdateSi = this.createOrUpdateSi(blnoInfoVO, true, userId);
        }
        // 如果是 si 更改生成的临时数据  状态修改为已确认 结束
        if (SiValidEnum.PENDING.getValue().equals(blnoBasInfoVO.getValid())) {
            LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
            lambdaUpdate.eq(BlnoDO::getId, orUpdateSi)
                    .set(BlnoDO::getSiStatus, SiStatusEnum.BookingPendingSpace.getValue());
            blnoMapper.update(null, lambdaUpdate);

            confirmNoticeService.createSingleNotice(orUpdateSi, ConfirmNoticeOperateEnum.APPROVAL.getValue()
                    , blnoBasInfoVO.getNoticeType());
            return orUpdateSi;
        }
        if (!StringUtils.equals(blnoBasInfoVO.getSiStatus(), SiStatusEnum.BookingDraft.getValue())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CAN_NOT_COMMIT);
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.eq(BlnoDO::getId, blnoBasInfoVO.getId() == null ? orUpdateSi : blnoBasInfoVO.getId())
                .set(BlnoDO::getSiStatus, SiStatusEnum.BookingProcessing.getValue())
                .set(BlnoDO::getSignOrderTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        blnoBasInfoVO.setId(orUpdateSi);
        saveOperateLog(blnoBasInfoVO, SI_OPERATE_TYPE, SUBMIT_SI.getValue(), SUBMIT_SI.getLabel());
        //异步生成疑似危险品记录  只有 RD 和 DG的货类需要处理
        String cargoType = blnoInfoVO.getBlnoCargos().get(0).getCargoType();
        if(CargoTypeEnum.DG.getValue().equals(cargoType) || CargoTypeEnum.RD.getValue().equals(cargoType)){
            Map<String, String> blnoCommodityEnMap = MapUtil.newHashMap(16);
            blnoCommodityEnMap.put(blnoBasInfoVO.getBlNo(),blnoInfoVO.getBlnoCargos().get(0).getCommodityEn());
            taskExecutor.execute(() -> {
                this.saveSuspectedDg(blnoCommodityEnMap);
            });
        }
        return orUpdateSi;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitSi(BlnoInfoVO blnoInfoVO) {
        return commitSi(blnoInfoVO, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
    }

    private Long saveOperateLog(BlnoBaseInfoVO baseInfoVO, String operateType, String operateTypeSub,
                                String operateDesc) {
        OperateLogCreateReqVO operateLog = new OperateLogCreateReqVO();
        operateLog.setBusinessId(baseInfoVO.getId());
        operateLog.setBusinessNo(baseInfoVO.getBookingNo());
        operateLog.setBusinessType(BusinessTypeEnum.SI.getValue());
        operateLog.setOperateType(operateType);
        operateLog.setOperateTypeSub(operateTypeSub);
        operateLog.setOperateDesc(operateDesc);
        return operateLogService.createOperateLog(operateLog);
    }

    @Override
    public BlnoDO createOrUpdateSi(BlnoBaseInfoVO createReqVO) {
        // 插入 todo
        BlnoDO blnoDO = BeanUtil.copyProperties(createReqVO, BlnoDO.class, "createTime", "updateTime", "creator", "updater");
        blnoDO.setCurrentPort(blnoDO.getPolCode());
        blnoDO.setSplitCombineType(StringUtils.isEmpty(blnoDO.getSplitCombineType()) ? SiSplitCombineEnum.NORMAL.getValue() : blnoDO.getSplitCombineType());
        if (createReqVO.getId() != null) {
            validateBlnoExists(createReqVO.getId());
            blnoDO.setPodBlFlag(true);
            blnoMapper.updateById(blnoDO);
        } else {
            // 默认状态
            blnoDO.setPodBlFlag(true);
            blnoDO.setSiStatus(SiStatusEnum.BookingDraft.getValue());
            blnoMapper.insert(blnoDO);
        }
        return blnoDO;
    }

    @Override
    public List<BlnoPageRespVO> getExportList(BlnoBaseInfoExportVO blnoBaseInfoExportVO) {
        BlnoPageReqVO blnoPageReqVO = BeanUtil.copyProperties(blnoBaseInfoExportVO, BlnoPageReqVO.class);
        blnoPageReqVO.setPageNo(1);
        blnoPageReqVO.setPageSize(1000);
        PageResult<BlnoPageRespVO> blnoPage = this.getBlnoPage(blnoPageReqVO);
        if (blnoPage != null && CollectionUtil.isNotEmpty(blnoPage.getList())) {
            List<BlnoPageRespVO> list = blnoPage.getList();
            list.forEach(n -> {
                if (SiStatusEnum.BookingDraft.getValue().equals(n.getSiStatus())) {
                    n.setSiStatus(SiStatusEnum.BookingDraft.getLabel());
                } else if (SiStatusEnum.BookingProcessing.getValue().equals(n.getSiStatus())) {
                    n.setSiStatus(SiStatusEnum.BookingProcessing.getLabel());
                } else if (SiStatusEnum.BookingPendingSpace.getValue().equals(n.getSiStatus())) {
                    n.setSiStatus(SiStatusEnum.BookingPendingSpace.getLabel());
                } else if (SiStatusEnum.AwaitingMaterialUploaded.getValue().equals(n.getSiStatus())) {
                    n.setSiStatus(SiStatusEnum.AwaitingMaterialUploaded.getLabel());
                } else if (SiStatusEnum.BookingConfirmed.getValue().equals(n.getSiStatus())) {
                    n.setSiStatus(SiStatusEnum.BookingConfirmed.getLabel());
                } else if (BookingRejected.getValue().equals(n.getSiStatus())) {
                    n.setSiStatus(BookingRejected.getLabel());
                }
            });
            return list;
        } else {
            return new ArrayList<BlnoPageRespVO>();
        }
    }

    @Override
    public BlnoInfoVO getSiDetail(Long blnoId) {
        return this.getDetail(blnoId);
    }

    @Override
    public List<BlnoInfoVO> getSiDetailListByBlNos(List<String> blNos) {
        return this.getDetailListByBlNos(blNos);
    }

    @Override
    public List<BlnoDO> getBlnoWithFreightList(BlnoFreightCalReqVO reqVO) {
        return getBlnoWithFreightList(reqVO, null);
    }

    @Override
    public List<BlnoDO> getBlnoWithFreightList(BlnoFreightCalReqVO reqVO, Boolean blLockFlag) {
        List<String> rejectStatuList = new ArrayList<>();
        rejectStatuList.add(SiStatusEnum.BookingDraft.getValue());
        rejectStatuList.add(SiStatusEnum.BookingProcessing.getValue());

        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eqIfPresent(BlnoDO::getCurrentPort, reqVO.getCurrentPort())
                .eqIfPresent(BlnoDO::getTopAgentCode, reqVO.getTopAgentCode())
                .eqIfPresent(BlnoDO::getBkgVesselCode, reqVO.getBkgVesselCode())
                .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                .eqIfPresent(BlnoDO::getBkgVoyage, reqVO.getBkgVoyage()).eqIfPresent(BlnoDO::getPolCode, reqVO.getPolCode())
                .eqIfPresent(BlnoDO::getBlNo, reqVO.getBlNo()).eqIfPresent(BlnoDO::getSpaceOwner, reqVO.getSpaceOwner())
                .eqIfPresent(BlnoDO::getDelCode, reqVO.getDelCode()).eqIfPresent(BlnoDO::getBkpCode, reqVO.getBkpCode())
                .eqIfPresent(BlnoDO::getAgreementNo, reqVO.getAgreementNo()).eqIfPresent(BlnoDO::getBlLockFlag, blLockFlag)
                .notIn(BlnoDO::getSiStatus, rejectStatuList).eq(BlnoDO::getPodBlFlag, true);
        return blnoMapper.selectList(queryWrapperX);
    }

    @Override
    public List<BlnoDO> getBlnoWithLclList() {
        List<String> rejectStatuList = new ArrayList<>();
        rejectStatuList.add(SiStatusEnum.BookingDraft.getValue());
        rejectStatuList.add(SiStatusEnum.BookingProcessing.getValue());

        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.notIn(BlnoDO::getSiStatus, rejectStatuList).eq(BlnoDO::getPodBlFlag, true).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue());
        return blnoMapper.selectList(queryWrapperX);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Lock4j(name = "siUnique", keys = {"'checkSiBcr'", "#blnoCheckSiReqVO.toString"})
    public BlnoCheckSiRespVO checkSi(BlnoCheckSiReqVO blnoCheckSiReqVO) {
        BlnoCheckSiRespVO blnoCheckSiRespVO = new BlnoCheckSiRespVO();
        Boolean basicCheckFlag = this.basicCheck(blnoCheckSiReqVO.getBlnoInfoVO(), blnoCheckSiRespVO, false);
        log.info("基本规则校验结果:{},校验明细阻断或警告:{}", basicCheckFlag, JSON.toJSONString(blnoCheckSiRespVO.getPrompts()));
        // check DCR
        SiDetailRespVO siDetailRespVO = checkSiDcr(blnoCheckSiReqVO.getBlnoInfoVO().getBlnoBasInfoVO().getId());
        blnoCheckSiRespVO.setSiBcrCheckBaseVos(siDetailRespVO.getDcrCheckList());
        blnoCheckSiRespVO.setCheckSuccessFlag(siDetailRespVO.getCheckBcr() && basicCheckFlag);
        return blnoCheckSiRespVO;
    }

    @Override
    @Lock4j(name = "siUnique", keys = {"'checkSiBcr'", "#siId"})
    public SiDetailRespVO checkSiDcr(Long blnoId) {
        BlnoInfoVO detail = getDetail(blnoId);
        SiDetailRespVO siDetailRespVO = transfer(detail);
        checkSiBcr(siDetailRespVO);
        return siDetailRespVO;

    }

    @Override
    public List<OperateLogRespVO> getSiOperationLog(Long blnoId) {
        return operateLogService.selectByBusinessId(BusinessTypeEnum.SI.getValue(), blnoId);
    }

    public void checkSiBcr(SiDetailRespVO siDetail) {
        BlnoBaseInfoVO si = siDetail.getBlnoBaseInfoVO();
        setSiVslvoyFirstLinerAndEtd(siDetail.getSiVslvoyList(), si);
        // 先删除历史BCR校验 物理删除
        soBcrCheckService.deleteByBusinessId(si.getId());
        List<LimitRuleRespVO> limitList = findRuleBySi(si);
        checkEtdTime(limitList, si);
        if (CollectionUtil.isEmpty(limitList)) {
            return;
        }
        // 查找规则详情
        limitRuleService.matchAttatchAndConfirmAndRuleItem(limitList);
        // 转换 拼接si的信息 字段信息
        Map<String, List<String>> fieldMap = SiFilterConditionFieldEnum.getAllField();
        List<Map<String, Object>> cargoMapList = new ArrayList<>();
        List<Map<String, Object>> vslvoyMapList = new ArrayList<>();
        List<Map<String, Object>> forecastMapList = new ArrayList<>();
        List<LimitRuleRespVO> bcrRuleList = new ArrayList<>();
        // 字段 过滤 同so 所以公用一套
        Map<String, Object> siMap = invokeGetValue(si, fieldMap.get("SI"));
        siDetail.getSiCargoList().forEach(e -> {
            cargoMapList.add(invokeGetValue(e, fieldMap.get("Cargo")));
        });
        siDetail.getSiVslvoyList().forEach(e -> {
            vslvoyMapList.add(invokeGetValue(e, fieldMap.get("Vslvoy")));
        });
        siDetail.getSiForecastList().forEach(e -> {
            forecastMapList.add(invokeGetValue(e, fieldMap.get("Forecast")));
        });
        limitList.forEach(e -> {
            if (checkRule(e, siMap, cargoMapList, vslvoyMapList, forecastMapList)) {
                // 规则匹配上
                bcrRuleList.add(e);
            }
        });
        // 统计校验规则 结果保存BCR校验规则
        List<SoBcrCheckCreateReqVO> createList = new ArrayList<>();
        Map<String, AttachmentRespVO> soAttachmentMap = siDetail.getAttachmentList().stream()
                .collect(Collectors.toMap(AttachmentRespVO::getAttachTypeCode, e -> e, (o1, o2) -> o1));
        Map<String, RequirementsRespVO> soRequirementMap =
                siDetail.getRequireList().stream().collect(Collectors.toMap(RequirementsRespVO::getRequireCode, e -> e, (o1, o2) -> o1));
        bcrRuleList.forEach(e -> {
            // 检验规则要求与现有的附件或要求是否匹配
            checkBcr(e, createList, si, soAttachmentMap, soRequirementMap);
        });
        if (CollectionUtil.isNotEmpty(createList)) {
            List<SoBcrCheckRespVO> bcrCheckList = soBcrCheckService.insertBatch(createList);
            // 添加BCR校验数据
            siDetail.setDcrCheckList(bcrCheckList);
        }
    }

    public void checkBcr(LimitRuleRespVO e, List<SoBcrCheckCreateReqVO> createList, BlnoBaseInfoVO si,
                         Map<String, AttachmentRespVO> soAttachmentMap, Map<String, RequirementsRespVO> soRequirementMap) {
        // 禁接 匹配上就默认不通过
        if (RULE_TYPE_PROHIBITED.equals(e.getRuleType())) {
            createList.add(new SoBcrCheckCreateReqVO(si.getId(), e.getId(), e.getRuleType(), e.getRuleName(),
                    e.getFeedbackContent(), false));
        }
        // 补充材料->提单附件表
        if (RULE_TYPE_SUPPLEMENTARY_MATERIALS.equals(e.getRuleType())) {
            if (CollectionUtil.isNotEmpty(e.getAttatchItemList())) {
                e.getAttatchItemList().forEach(a -> {
                    SoBcrCheckCreateReqVO checkAttatch = new SoBcrCheckCreateReqVO(si.getId(), e.getId(),
                            e.getRuleType(), e.getRuleName(), a.getReqMatCode(), a.getReqMatNameCn(), false);
                    if (soAttachmentMap.get(a.getReqMatCode()) != null) {
                        checkAttatch.setCheckStatus(true);
                    }
                    createList.add(checkAttatch);
                });
            } else {
                createList.add(new SoBcrCheckCreateReqVO(si.getId(), e.getId(), e.getRuleType(), e.getRuleName(),
                        e.getFeedbackContent(), false));
            }
        }
        // 确认项->订舱附加要求表
        if (RULE_TYPE_INFORMATION_CONFIRM.equals(e.getRuleType())) {
            if (CollectionUtil.isNotEmpty(e.getConfirmItemList())) {
                e.getConfirmItemList().forEach(a -> {
                    SoBcrCheckCreateReqVO checkAttatch = new SoBcrCheckCreateReqVO(si.getId(), e.getId(),
                            e.getRuleType(), e.getRuleName(), a.getConfirmCode(), a.getConfirmNameCn(), false);
                    if (soRequirementMap.get(a.getConfirmCode()) != null) {
                        checkAttatch.setCheckStatus(true);
                    }
                    createList.add(checkAttatch);
                });
            } else {
                createList.add(new SoBcrCheckCreateReqVO(si.getId(), e.getId(), e.getRuleType(), e.getRuleName(),
                        e.getFeedbackContent(), false));
            }
        }
    }

    public SiDetailRespVO transfer(BlnoInfoVO detail) {
        SiDetailRespVO siDetailRespVO = new SiDetailRespVO();
        siDetailRespVO.setBlnoBaseInfoVO(detail.getBlnoBasInfoVO());
        siDetailRespVO.setSiVslvoyList(detail.getBlnoVslvoys());
        siDetailRespVO.setSiForecastList(detail.getBlnoCntrs());
        siDetailRespVO.setSiCargoList(detail.getBlnoCargos());
        siDetailRespVO.setSiScn(detail.getBlnoScns());
        siDetailRespVO.setAttachmentList(BeanUtil.copyToList(detail.getAttachmentReqVO(), AttachmentRespVO.class));
        siDetailRespVO.setRequireList(BeanUtil.copyToList(detail.getReqVOList(), RequirementsRespVO.class));
        return siDetailRespVO;
    }

    public List<LimitRuleRespVO> findRuleBySi(BlnoBaseInfoVO si) {
        LimitRulePageReqVO reqVO = new LimitRulePageReqVO();
        return limitRuleService.findLimitRule(reqVO, "DCR");
    }

    public void setSiVslvoyFirstLinerAndEtd(List<BlnoVslvoyUpdateReqVO> siVslvoyList, BlnoBaseInfoVO e) {
        if (CollectionUtil.isEmpty(siVslvoyList)) {
            return;
        }
        siVslvoyList.forEach(t -> {
            if (TransmodeEnum.LINER.getValue().equals(t.getTransmode())) {
                if (e.getSiVslvoyFirstLiner() == null) {
                    e.setPolEtd(t.getPolEtd());
                    e.setSiVslvoyFirstLiner(t);
                }
                // 最后Liner
                e.setPodEta(t.getPodEta());
            }
        });
    }

    public Map<String, Object> invokeGetValue(Object info, List<String> fields) {
        Map<String, Object> valueMap = new HashMap<>(16);
        BeanMap beanMap = new org.apache.commons.beanutils.BeanMap(info);
        for (String field : fields) {
            Object obj = beanMap.get(field);
            if (obj != null) {
                valueMap.put(field, obj);
            }
        }
        return valueMap;
    }

    public void checkEtdTime(List<LimitRuleRespVO> limitList, BlnoBaseInfoVO si) {
        ZoneId zoneId = ZoneId.systemDefault();
        for (int i = 0; i < limitList.size(); i++) {
            LimitRuleRespVO rule = limitList.get(i);
            // 预计离港时间 是否符合 实际离港ATD目前没法判断
            if ("ETD".equals(rule.getEffectiveDateType()) && StringUtils.isNotEmpty(si.getPolEtd())) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
                LocalDateTime polEtdDate = LocalDateTime.parse(si.getPolEtd(), formatter);
                if (rule.getEffictiveDate() != null) {
                    LocalDate localDate = rule.getEffictiveDate().toInstant().atZone(zoneId).toLocalDate();
                    LocalDateTime effictiveDate = LocalDateTime.of(localDate, LocalTime.MIN);
                    if (effictiveDate.isAfter(polEtdDate)) {
                        limitList.remove(i);
                        i--;
                        continue;
                    }
                }
                if (rule.getExpiringDate() != null) {
                    LocalDate localDate = rule.getExpiringDate().toInstant().atZone(zoneId).toLocalDate();
                    LocalDateTime expiringDate = LocalDateTime.of(localDate, LocalTime.MAX);
                    if (expiringDate.isBefore(polEtdDate)) {
                        limitList.remove(i);
                        i--;
                    }
                }
            }
        }
    }

    public boolean checkRule(LimitRuleRespVO rule, Map<String, Object> siMap, List<Map<String, Object>> cargoMapList,
                             List<Map<String, Object>> vslvoyMapList, List<Map<String, Object>> forecastMapList) {
        if (rule.getRuleItem() == null) {
            return true;
        }
        for (int i = 0; i < cargoMapList.size(); i++) {
            Map<String, Object> cargoMapTemp = cargoMapList.get(i);
            for (int j = 0; j < vslvoyMapList.size(); j++) {
                Map<String, Object> vslvoyMapTemp = vslvoyMapList.get(j);
                for (int k = 0; k < forecastMapList.size(); k++) {
                    Map<String, Object> forecastMapTemp = forecastMapList.get(k);
                    Map<String, Object> allTemp = new HashMap<>(16);
                    allTemp.putAll(siMap);
                    allTemp.putAll(cargoMapTemp);
                    allTemp.putAll(vslvoyMapTemp);
                    allTemp.putAll(forecastMapTemp);
                    boolean check = CmclinkExpressionUtil.check(rule.getRuleItem().getMatchExpression(), allTemp);
                    // 判断通过 BCR留档
                    if (check) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public Boolean basicCheck(BlnoInfoVO blnoInfoVO, BlnoCheckSiRespVO blnoCheckSiRespVO, Boolean checkFlag) {
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        BlnoDO blnoDO = blnoMapper.selectById(blnoBasInfoVO.getId());
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        if (!checkFlag && !StringUtils.equals(blnoDO.getSiStatus(), SiStatusEnum.BookingProcessing.getValue())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CAN_NOT_CHECK_CONFIRM);
        }
        List<BlnoPaymentUpdateReqVO> blnoPayments = blnoInfoVO.getBlnoPayments();
        List<BlnoVslvoyUpdateReqVO> blnoVslvoys = blnoInfoVO.getBlnoVslvoys();
        List<BlnoScnUpdateReqVO> blnoScns = blnoInfoVO.getBlnoScns();
        List<BlnoCargoUpdateReqVO> blnoCargos = blnoInfoVO.getBlnoCargos();
        List<BlnoCntrUpdateReqVO> blnoCntrs = blnoInfoVO.getBlnoCntrs();
        List<AttachmentUpdateReqVO> attachmentReqVos = blnoInfoVO.getAttachmentReqVO();
        if (CollectionUtil.isEmpty(blnoPayments)) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_PAYMENT_NOT_EXISTED);
        }
        // 指定支付方式中是否包含海运费
        if (blnoPayments.stream()
                .noneMatch(entity -> StringUtils.equals(entity.getChargeCode(), SiPaymentConstants.CHARGE_CODE_FRT))) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_FRT_NOT_EXISTED);
        }
        if (CollectionUtil.isEmpty(blnoScns)) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SCN_NOT_EXISTED);
        }
        if (CollectionUtil.isEmpty(blnoCargos)) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CARGO_NOT_EXISTED);
        }
        if (CollectionUtil.isEmpty(blnoCntrs)) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CNTR_NOT_EXISTED);
        }
        if (!checkFlag) {
            //是否跳过cpf校验
            checkCpfCode(blnoInfoVO.getBlnoScns());
            this.checkCntrSiInSo(Arrays.asList(blnoBasInfoVO.getBookingNo()), blnoCntrs);
        }
        // 返回 的校验提示信息列表
        List<BlnoCheckSiErrorPrompt> checkSiErrorPrompts = new ArrayList<>();
        blnoCheckSiRespVO.setPrompts(checkSiErrorPrompts);
        // 存在 阻断性的校验标记
        AtomicReference<Boolean> basicCheckFlag = new AtomicReference<>(true);
        // 件 重 尺 校验 非阻断
        BlnoCargoUpdateReqVO blnoCargoUpdateReqVO = blnoCargos.get(0);
        if (ObjectUtil.isNull(blnoCargoUpdateReqVO.getQuantity())
                || ObjectUtil.isNull(blnoCargoUpdateReqVO.getMeasurement())
                || ObjectUtil.isNull(blnoCargoUpdateReqVO.getGrossWeight())) {
            throw ServiceExceptionUtil.exception(BlnoCargoErrorCodeConstants.BLNO_CARGO_QUANTITY_NOT_EXISTS);
        }
        BigDecimal totalCntrWeight =
                blnoCntrs.stream().map(BlnoCntrUpdateReqVO::getGrossWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalCntrMeasurement =
                blnoCntrs.stream().map(BlnoCntrUpdateReqVO::getMeasurement).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (blnoInfoVO.getBlnoCntrs().stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum() != blnoCargoUpdateReqVO
                .getQuantity() || (blnoCargoUpdateReqVO.getGrossWeight().compareTo(totalCntrWeight) != 0)
                || (blnoCargoUpdateReqVO.getMeasurement().compareTo(totalCntrMeasurement) != 0)) {
            checkSiErrorPrompts
                    .add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_ONE.getPromptCode(), CHECK_PROMPT_ONE.getPromptDesc(), CHECK_PROMPT_ONE.getPromptType()));
        }
        // 检查提单全角字符。 检查提单货描（英文品名）、唛头、。 阻断
        blnoCargos.stream().anyMatch(n -> {
            if (CheckUtil.containsNoEnglish(n.getCommodityEn()) || CheckUtil.containsNoEnglish(n.getMarks())
                    || CheckUtil.isQuanJiaoValid(n.getCommodityEn()) || CheckUtil.isQuanJiaoValid(n.getMarks())) {
                checkSiErrorPrompts.add(
                        new BlnoCheckSiErrorPrompt(CHECK_PROMPT_TWO.getPromptCode(), CHECK_PROMPT_TWO.getPromptDesc(), CHECK_PROMPT_TWO.getPromptType()));
                basicCheckFlag.set(false);
                return true;
            }
            return false;
        });

        // Check OOG 如果箱型是 OT、FR 并且箱状态是 F，必须有 OOG 信息。前、后、左、右、上都不能为空。 阻断
        if (blnoBasInfoVO.getCntrStatus().equals(CommonConstants.F)) {
            blnoCntrs.stream()
                    .filter(n -> StringUtils.equals(n.getCntrType(), "OT") || StringUtils.equals(n.getCntrType(), "FR"))
                    .anyMatch(n -> {
                        if (ObjectUtil.isNull(n.getOverAfter()) || ObjectUtil.isNull(n.getOverForward())
                                || ObjectUtil.isNull(n.getOverHeight()) || ObjectUtil.isNull(n.getOverLeft())
                                || ObjectUtil.isNull(n.getOverRight())) {
                            checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_THREE.getPromptCode(),
                                    CHECK_PROMPT_THREE.getPromptDesc(), CHECK_PROMPT_THREE.getPromptType()));
                            basicCheckFlag.set(false);
                            return true;
                        }
                        return false;
                    });
        }

        // 如箱号前缀在如下列表 提示可能涉嫌违反伊朗制裁 阻断
        List<String> dictKeysList = dictDataService.getDictKeysList(DOC_IRAN_SANCTIONS_RELATED_CNTR_PREFIX);
        blnoCntrs.stream().anyMatch(n -> {
            if(n.getCntrNo().length() < 4){
                return false;
            }
            if (dictKeysList.contains(n.getCntrNo().substring(0, 4))) {
                checkSiErrorPrompts.add(
                        new BlnoCheckSiErrorPrompt(CHECK_PROMPT_FOUR.getPromptCode(), CHECK_PROMPT_FOUR.getPromptDesc(), CHECK_PROMPT_FOUR.getPromptType()));
                basicCheckFlag.set(false);
                return true;
            }
            return false;
        });
        // 检查箱号是否正确。11 位长度箱号有校验规则前四位字母后六位数字最后一位验证码也是数字。这个校验接口调用箱管的接口  非阻断
        blnoCntrs.stream().anyMatch(n -> {
            if (!CheckUtil.isValidCntrNo(n.getCntrNo())) {
                checkSiErrorPrompts.add(
                        new BlnoCheckSiErrorPrompt(CHECK_PROMPT_FIVE.getPromptCode(), CHECK_PROMPT_FIVE.getPromptDesc(), CHECK_PROMPT_FIVE.getPromptType()));
                return true;
            }
            return false;
        });
        // 检查提单路径船舶代码不为空，进口航次为空则校验不通过。 阻断
        blnoVslvoys.stream().anyMatch(n -> {
            if (StringUtils.isNotEmpty(n.getVesselCode()) && StringUtils.isEmpty(n.getVoyageIm())) {
                checkSiErrorPrompts.add(
                        new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SIX.getPromptCode(), CHECK_PROMPT_SIX.getPromptDesc(), CHECK_PROMPT_SIX.getPromptType()));
                basicCheckFlag.set(false);
                return true;
            }
            return false;
        });
        // Check LCL data 拼箱数据校验 阻断
        if (!checkFlag) {
            //是否需要跳过LCL data校验
            lclCheck(blnoInfoVO, checkSiErrorPrompts, basicCheckFlag);
        }
        // 获取vgm 数据
        if (!checkFlag) {
            List<BlnoCntrUpdateReqVO> vgmCntrs = blnoCntrService.getBlnoCntrNewByBlnoId(blnoDO.getId(), blnoDO.getBkgVesselCode(), blnoDO.getBkgVoyage());

            // 检查是否有 VGM 数据 提示
            vgmCntrs.stream().anyMatch(n -> {
                if (ObjectUtil.isNull(n.getVgmWeight()) && ObjectUtil.isNull(n.getVgmTypeCode())) {
                    checkSiErrorPrompts.add(
                            new BlnoCheckSiErrorPrompt(CHECK_PROMPT_EIGHT.getPromptCode(), CHECK_PROMPT_EIGHT.getPromptDesc(), CHECK_PROMPT_EIGHT.getPromptType()));
                    return true;
                }
                return false;
            });

            // 检查 VGM 重量和箱信息重量的一致性 提示
            vgmCntrs.stream().anyMatch(n -> {
                if (!checkVgm(n)) {
                    checkSiErrorPrompts.add(
                            new BlnoCheckSiErrorPrompt(CHECK_PROMPT_NINE.getPromptCode(), CHECK_PROMPT_NINE.getPromptDesc(), CHECK_PROMPT_NINE.getPromptType()));
                    return true;
                }
                return false;
            });
        }

        // 如果目的港是越南，检查是否符合越南舱单规则：收货人、发货人的名称不能超过 256 个字节。 阻断
        if (blnoBasInfoVO.getDelCode().startsWith(YUE_NAN)) {
            blnoScns.stream().filter(n -> StringUtils.equals(n.getPartyTypeCode(), PartyTypeCodeEnum.SHIPPER.getValue())
                    || StringUtils.equals(n.getPartyTypeCode(), PartyTypeCodeEnum.CONSIGNEE.getValue())).anyMatch(m -> {
                if (!isValidString(m.getCompanyName())) {
                    checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_TEN.getPromptCode(),
                            CHECK_PROMPT_TEN.getPromptDesc(), CHECK_PROMPT_TEN.getPromptType()));
                    basicCheckFlag.set(false);
                    return true;
                }
                return false;
            });
        }

        // 货物是否填写hscode 阻断
        blnoCargos.stream().anyMatch(n -> {
            if (StringUtils.isEmpty(n.getHsCode())) {
                checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_ELEVEN.getPromptCode(),
                        CHECK_PROMPT_ELEVEN.getPromptDesc(), CHECK_PROMPT_ELEVEN.getPromptType()));
                basicCheckFlag.set(false);
                return true;
            }
            return false;
        });

        // 检查是否符合印度舱单规则。 阻断
        if (blnoBasInfoVO.getPodCode().startsWith(INDIA_PREFEX)) {
            blnoScns.stream().filter(n -> PartyTypeCodeEnum.NOTIFY.getValue().equals(n.getPartyTypeCode()) &&
                    !BlnoScnSpecialCompanyConstants.SAME_SACONSIGNEE.equals(n.getCompanyName())).forEach(n -> {
                        if (StringUtils.isEmpty(n.getEmail())) {
                            checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_TWELVE.getPromptCode(),
                                    CHECK_PROMPT_TWELVE.getPromptDesc(), CHECK_PROMPT_TWELVE.getPromptType()));
                            basicCheckFlag.set(false);
                        }
                    }
            );
        }


        // 提单主体校验 阻断
     /*     if (StringUtils.isEmpty(blnoBasInfoVO.getBlEntity())) {
            checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_THIRTEEN.getPromptCode(),
                    CHECK_PROMPT_THIRTEEN.getPromptDesc(), CHECK_PROMPT_THIRTEEN.getPromptType()));
            basicCheckFlag.set(false);
        }*/

        // 台湾地区TW卸港 强制拦截： 港口描述/收发通名称/收发通地址/货描/唛头 不允许包含R.O.C （Republic Of China） 阻断
        if (blnoBasInfoVO.getPodCode().startsWith(TAI_WAN)) {
            boolean flag1 = blnoScns.stream().filter(m -> m.getCompanyName().contains(BlnoRocConstants.ROC)
                    || m.getCompanyAddress().contains(BlnoRocConstants.ROC)).findAny().isPresent();
            boolean flag2 = blnoCargos.stream().filter(
                            m -> m.getMarks().contains(BlnoRocConstants.ROC) || m.getCommodityEn().contains(BlnoRocConstants.ROC))
                    .findAny().isPresent();
            if (blnoBasInfoVO.getPodDesc().contains(BlnoRocConstants.ROC) || flag1 || flag2) {
                checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_FOURTEEN.getPromptCode(),
                        CHECK_PROMPT_FOURTEEN.getPromptDesc(), CHECK_PROMPT_FOURTEEN.getPromptType()));
                basicCheckFlag.set(false);
            }
        }

        // 泰国TH卸港唛头不能包含NOMARK和N/M 阻断
        if (blnoBasInfoVO.getPodCode().startsWith(TAI_GUO)) {
            blnoCargos.stream().anyMatch(m -> {
                String result = m.getMarks().toUpperCase().replaceAll("\\s+", "");
                boolean check = StringUtils.isNotEmpty(m.getMarks()) && (result.contains(ThailandPortConstants.NM)
                        || result.contains(ThailandPortConstants.NOMARK) || result.contains(ThailandPortConstants.NOMARKS));
                if (check) {
                    checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_FIFTEEN.getPromptCode(),
                            CHECK_PROMPT_FIFTEEN.getPromptDesc(), CHECK_PROMPT_FIFTEEN.getPromptType()));
                    basicCheckFlag.set(false);
                    return true;
                }
                return false;
            });
        }

        // 货类和箱型选择冲突判断（货类是 RF 或 RD，箱型必须是 RF 或 RH ；货类必须是AK，箱型为 FR 或 OT） 阻断
        if (CargoTypeEnum.RF.getValue().equals(blnoCargos.get(0).getCargoType())
                || CargoTypeEnum.RD.getValue().equals(blnoCargos.get(0).getCargoType())) {
            blnoCntrs.stream().anyMatch(n -> {
                if (!n.getCntrType().equals(CNTR_TYPE_RF) && !n.getCntrType().equals(CNTR_TYPE_RH)) {
                    checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SIXTEEN.getPromptCode(),
                            CHECK_PROMPT_SIXTEEN.getPromptDesc(), CHECK_PROMPT_SIXTEEN.getPromptType()));
                    basicCheckFlag.set(false);
                    return true;
                }
                return false;
            });
        } else if (CargoTypeEnum.AK.getValue().equals(blnoCargos.get(0).getCargoType())) {
            blnoCntrs.stream().anyMatch(n -> {
                if (!n.getCntrType().equals(CNTR_TYPE_FR) && !n.getCntrType().equals(CNTR_TYPE_OT) && !n.getCntrType().equals(CNTR_TYPE_HC)) {
                    checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SIXTEEN.getPromptCode(),
                            CHECK_PROMPT_SIXTEEN.getPromptDesc(), CHECK_PROMPT_SIXTEEN.getPromptType()));
                    basicCheckFlag.set(false);
                    return true;
                }
                return false;
            });
        }

        // 收发通名称、地址、邮箱、电话、传真是否含有非英文字符 阻断
        blnoScns.stream().anyMatch(n -> {
            if (CheckUtil.containsNoEnglish(n.getCompanyName()) || CheckUtil.containsNoEnglish(n.getCompanyAddress())
                    || CheckUtil.containsNoEnglish(n.getEmail()) || CheckUtil.containsNoEnglish(n.getFax())
                    || CheckUtil.containsNoEnglish(n.getTelephone()) || CheckUtil.isQuanJiaoValid(n.getCompanyName())
                    || CheckUtil.isQuanJiaoValid(n.getCompanyAddress()) || CheckUtil.isQuanJiaoValid(n.getEmail())
                    || CheckUtil.isQuanJiaoValid(n.getFax()) || CheckUtil.isQuanJiaoValid(n.getTelephone())) {
                checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SEVENTEEN.getPromptCode(),
                        CHECK_PROMPT_SEVENTEEN.getPromptDesc(), CHECK_PROMPT_SEVENTEEN.getPromptType()));
                basicCheckFlag.set(false);
                return true;
            }
            return false;
        });

        if (!checkFlag) {
            //是否上传VGM附件  非阻断
            if (CollectionUtil.isNotEmpty(attachmentReqVos)) {
                if (attachmentReqVos.stream().allMatch(n -> !VGM.equals(n.getAttachTypeCode()))) {
                    checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_EIGHTEEN.getPromptCode(),
                            CHECK_PROMPT_EIGHTEEN.getPromptDesc(), CHECK_PROMPT_EIGHTEEN.getPromptType()));
                }
            }
        }
        return basicCheckFlag.get();
    }

    public void checkCpfCode(List<BlnoScnUpdateReqVO> blnoScns) {
        Boolean flag = blnoScns.stream().filter(n -> !(PartyTypeCodeEnum.CONSIGNEE.getValue().equals(n.getPartyTypeCode())
                        && (StringUtils.isNotEmpty(n.getCompanyName()) && n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_ORDER))))
                .anyMatch(n -> StringUtils.isEmpty(n.getCsmCode()));
        if (flag) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.EQU_COMMIT_NOT_CPF);
        }
        List<String> uniqueCsmCodes = blnoScns.stream().filter(n -> StringUtils.isNotEmpty(n.getCsmCode())).map(BlnoScnUpdateReqVO::getCsmCode).distinct().collect(Collectors.toList());
        CompanyNameCastReqVO companyNameCastReqVO = new CompanyNameCastReqVO();
        companyNameCastReqVO.setCsmCodeList(uniqueCsmCodes);
        List<CompanyNameCastRespDTO> companyNameCastRespDtos = nameTransferService.customerFromCodeList(companyNameCastReqVO);
        if (uniqueCsmCodes.size() != companyNameCastRespDtos.size()) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SCN_CPF_NOT_EXISTED);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setDO(BlnoOperateDoReqVO blnoOperateReqVO) {
        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoOperateReqVO.getBlnoIds());
        for (BlnoDO blnoDO : blnoDOList) {
            if (StringUtils.isBlank(blnoDO.getSignType())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NO_SIGN_TYPE);
            }
            //电子提单没有交单，不能D/O（msg：The electronic bill of lading has not been returned!）；  todo 依赖外部未完成接口

            //未锁D/O (do_lock='N’)不能Set D/O（msg：没有锁定D/O,不能放单！）；
            if (blnoDO.getDoLock() == null || !blnoDO.getDoLock()) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_NOT_LOCK);
            }
            //判断应收费用是否已收费，未收费时提示存在费用未收情况，不允许设置D/O;  todo 依赖外部未完成接口

            if (CNTXG.equals(blnoDO.getPodCode())) {
                if (!blnoOperateReqVO.getDoWhenDepositHasBeenCollected()) {
                    //todo 再次调用获取收费信息 来确认
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NOT_CHECK_DEPOSIT_HAS_BEEN_COLLECTED);
                }
            }
            if (StringUtils.isNotBlank(blnoDO.getDoStatus())) {
                Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_IS_DO.getCode(), blnoDO.getBlNo());
            }
            blnoDO.setDoStatus(blnoOperateReqVO.getDoStatus());

        }

        //发送EDO tod
        if (SEND_EDO_PORT.contains(blnoOperateReqVO.getCurrentPort())) {
            this.sendEdo(blnoDOList, blnoOperateReqVO, DoOperationTypeEnum.SET_DO.getCode());
        }

        if (CollectionUtil.isNotEmpty(blnoDOList)) {
            blnoMapper.updateBatch(blnoDOList);
        }

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_TWELVE.getValue(),
                SiOperateTypeEnum.OPERATE_SI_TWELVE.getLabel(), "");
    }


    @Override
    public void sendEdo(BlnoOperateIdsReqVO blnoOperateIdsReqVO, DoOperationTypeEnum doOperationTypeEnum) {

        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoDO::getId, blnoOperateIdsReqVO.getBlnoIds());
        queryWrapperX.eq(BlnoDO::getValid, SiValidEnum.VALID.getValue());
        List<BlnoDO> blnoDOList = blnoMapper.selectList(queryWrapperX);
        if (!blnoDOList.isEmpty()) {
            this.sendEdo(blnoDOList, blnoOperateIdsReqVO, doOperationTypeEnum.getCode());
        }
    }

    private void sendEdo(List<BlnoDO> blnoDOList, BlnoOperateIdsReqVO blnoOperateIdsReqVo, String doOpType) {
        //发送EDO tod
        if (SEND_EDO_PORT.contains(blnoOperateIdsReqVo.getCurrentPort())) {
            blnoSendEdoService.sendEdo(this.getSiDetailListByBlNos(blnoDOList.stream().map(BlnoDO::getBlNo).collect(Collectors.toList())),
                    blnoOperateIdsReqVo, doOpType);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancelDO(BlnoOperateCancelDoReqVO blnoOperateReqVO) {
        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoOperateReqVO.getBlnoIds());
        for (BlnoDO blnoDO : blnoDOList) {
            if (StringUtils.isBlank(blnoDO.getDoStatus())) {
                Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_IS_NOT_DO.getCode(), blnoDO.getBlNo());
            }
        }

        if (CollectionUtil.isNotEmpty(blnoDOList)) {
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                    .set(BlnoDO::getDoStatus, null)
            );
        }
        if (SEND_EDO_PORT.contains(blnoOperateReqVO.getCurrentPort())) {
            this.sendEdo(blnoDOList, blnoOperateReqVO, DoOperationTypeEnum.CANCEL_DO.getCode());
        }

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_THIRTEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_THIRTEEN.getLabel(), "");
    }

    @Override
    public void notedTelex(BlnoOperateIdsReqVO blnoOperateReqVO) {
        confirmNoticeMapper.update(null, new LambdaUpdateWrapper<ConfirmNoticeDO>()
                .in(ConfirmNoticeDO::getBlnoId, blnoOperateReqVO.getBlnoIds())
                .set(ConfirmNoticeDO::getStatus, ConfirmNoticeStatusEnum.NOTED.getValue()));

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_FOURTEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_FOURTEEN.getLabel(), "");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lockDO(BlnoOperateLockDoReqVO blnoOperateReqVO) {
        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoOperateReqVO.getBlnoIds());

        if (CollectionUtil.isNotEmpty(blnoDOList)) {
            for (BlnoDO blnoDO : blnoDOList) {
                if (blnoDO.getDoLock() != null && blnoDO.getDoLock()) {
                    Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_DO_LOCK_ERROR.getCode(), blnoDO.getBlNo());
                }
            }
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                    .set(BlnoDO::getDoLock, true)
            );

            if (SEND_EDO_PORT.contains(blnoOperateReqVO.getCurrentPort())) {
                this.sendEdo(blnoDOList, blnoOperateReqVO, DoOperationTypeEnum.LOCK_DO.getCode());
            }
        }

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_FIFTEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_FIFTEEN.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lockDO(List<VoyageLockImReqVO> reqVos) {
        for (VoyageLockImReqVO reqVo : reqVos) {

            List<BlnoDO> blnoDOList = blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>()
                    .eq(BlnoDO::getBkgVesselCode, reqVo.getVesselCode())
                    .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                    .eq(BlnoDO::getBkgVoyage, reqVo.getVoyage())
                    .eq(BlnoDO::getPodCode, reqVo.getPortCode())
            );
            //按照getDoLock进行分组
            Map<Boolean, List<BlnoDO>> groupedByDoLock = blnoDOList.stream()
                    .collect(Collectors.groupingBy(
                            blno -> Boolean.TRUE.equals(blno.getDoLock()),
                            Collectors.toList()
                    ));

            List<BlnoDO> lockedBlnoList = groupedByDoLock.getOrDefault(true, Collections.emptyList());
            List<BlnoDO> unlockedBlnoList = groupedByDoLock.getOrDefault(false, Collections.emptyList());


            if (CollectionUtil.isNotEmpty(unlockedBlnoList)) {
                List<Long> blnoIds = unlockedBlnoList.stream().map(BlnoDO::getId).collect(Collectors.toList());
                blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                        .in(BlnoDO::getId, blnoIds)
                        .set(BlnoDO::getDoLock, true)
                );

                saveOperateLogBatch(blnoIds, SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_FIFTEEN.getValue(),
                        SiOperateTypeEnum.OPERATE_SI_FIFTEEN.getLabel(), "");
            }
//            if (!lockedBlnoList.isEmpty()) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_LOCK_ERROR
//                        , lockedBlnoList.stream().map(BlnoDO::getBlNo).collect(Collectors.joining( ",")));
//            }
        }
    }

    @Override
    public void unLockDO(BlnoOperateIdsReqVO blnoOperateReqVO) {
        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoOperateReqVO.getBlnoIds());
        for (BlnoDO blnoDO : blnoDOList) {
            if (doInfoMapper.exists(new LambdaQueryWrapper<DoInfoDO>()
                    .eq(DoInfoDO::getBlnoId, blnoDO.getId())
                    .isNotNull(DoInfoDO::getDoStatus)
            )) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHANGE_ERROR, blnoDO.getBlNo());
            }
        }
        if (CollectionUtil.isNotEmpty(blnoDOList)) {
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                    .set(BlnoDO::getDoLock, false)
            );
        }

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_SIXTEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_SIXTEEN.getLabel(), "");
    }

    @Override
    public Map<String, List<BlnoDO>> findBlnoByBlNos(List<String> blNoList) {
        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoDO::getBlNo, blNoList);
        return blnoMapper.selectList(queryWrapperX).stream().collect(Collectors.groupingBy(BlnoDO::getBlNo));
    }

    @Override
    public void unLockDO(List<VoyageLockImReqVO> reqVos) {
        for (VoyageLockImReqVO reqVo : reqVos) {

            List<BlnoDO> blnoDOList = blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>()
                    .eq(BlnoDO::getBkgVesselCode, reqVo.getVesselCode())
                    .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                    .eq(BlnoDO::getBkgVoyage, reqVo.getVoyage())
                    .eq(BlnoDO::getPodCode, reqVo.getPortCode())
            );
            for (BlnoDO blnoDO : blnoDOList) {
                if (doInfoMapper.exists(new LambdaQueryWrapper<DoInfoDO>()
                        .eq(DoInfoDO::getBlnoId, blnoDO.getId())
                        .isNotNull(DoInfoDO::getDoStatus)
                )) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHANGE_ERROR, blnoDO.getBlNo());
                }
            }
            if (CollectionUtil.isNotEmpty(blnoDOList)) {
                List<Long> blnoIds = blnoDOList.stream().map(BlnoDO::getId).collect(Collectors.toList());

                blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                        .in(BlnoDO::getId, blnoIds)
                        .set(BlnoDO::getDoLock, false)
                );
                saveOperateLogBatch(blnoIds, SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_SIXTEEN.getValue(),
                        SiOperateTypeEnum.OPERATE_SI_SIXTEEN.getLabel(), "");
            }


        }

    }

    @Override
    public void lockCharge(BlnoOperateLockChargeReqVO blnoOperateReqVO) {
        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoOperateReqVO.getBlnoIds());
        for (BlnoDO blnoDO : blnoDOList) {
            if (blnoDO.getImPaymentLock() != null && blnoDO.getImPaymentLock()) {
                Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_DO_CHARGE_LOCK_ERROR.getCode(), blnoDO.getBlNo());
            }
            if (!blnoLocalChargeMapper.selectList(new LambdaQueryWrapper<BlnoLocalChargeDO>()
                    .eq(BlnoLocalChargeDO::getBlnoId, blnoDO.getId())).isEmpty()) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHARGE_ERROR,blnoDO.getBlNo(),"应收");
            }
            if (!blnoLocalCostExService.getLocalCostByBlnoId(blnoDO.getId(), ExImTypeEnum.IM.getValue()).isEmpty()) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHARGE_ERROR,blnoDO.getBlNo(),"应付");
            }
        }
        if (CollectionUtil.isNotEmpty(blnoDOList)) {
            blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                    .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                    .set(BlnoDO::getImPaymentLock, true)
            );
            //todo 发送财务接口
        }

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_SEVENTEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_SEVENTEEN.getLabel(), "");
    }

    @Override
    public BlnoCheckMsgReqVO lockChargeCheck(List<Long> blnoIds) {
        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoIds);
        List<String> msgList = new ArrayList<>();
        BlnoCheckMsgReqVO blnoCheckMsgReqVO = new BlnoCheckMsgReqVO();
        for (BlnoDO blnoDO : blnoDOList) {
            if (blnoDO.getImPaymentLock() != null && blnoDO.getImPaymentLock()) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHARGE_LOCK_ERROR,blnoDO.getBlNo());
                msgList.add(blnoDO.getBlNo());
            }
        }
        if (!msgList.isEmpty()) {
            blnoCheckMsgReqVO.setType("alert");
            blnoCheckMsgReqVO.setContent(String.join(",", msgList) + "费用已锁定，无需再次锁定！");
            return blnoCheckMsgReqVO;
        }

        for (BlnoDO blnoDO : blnoDOList) {
            //todo 批量查询
            if (blnoLocalChargeMapper.selectList(new LambdaQueryWrapper<BlnoLocalChargeDO>()
                    .eq(BlnoLocalChargeDO::getBlnoId, blnoDO.getId())).isEmpty()) {
                //todo 国际化
                msgList.add(blnoDO.getBlNo() + "无应收费用");
            } else if (blnoLocalCostExService.getLocalCostByBlnoId(blnoDO.getId(), ExImTypeEnum.IM.getValue()).isEmpty()) {
                msgList.add(blnoDO.getBlNo() + "无应付费用");
            }
        }
        blnoCheckMsgReqVO.setType("confirm");
        blnoCheckMsgReqVO.setContent(String.join(",", msgList));
        return blnoCheckMsgReqVO;
    }

    @Override
    public void setCommodityCn(List<Long> blnoIds, String commodityCn) {
        blnoCargoMapper.update(null, new LambdaUpdateWrapper<BlnoCargoDO>()
                .in(BlnoCargoDO::getBlnoId, blnoIds)
                .set(BlnoCargoDO::getCommodityCn, commodityCn));

        saveOperateLogBatch(blnoIds, SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_EIGHTEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_EIGHTEEN.getLabel(), "");
    }

    @Override
    public void setTransitTerm(BlnoOperateTransitTermReqVO blnoOperateReqVO) {

        //todo 验证是否更新更新人时间
        blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                .set(BlnoDO::getTransitTermIm, blnoOperateReqVO.getTransitTerm()));

        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_NINETEEN.getValue(),
                SiOperateTypeEnum.OPERATE_SI_NINETEEN.getLabel(), "");
    }

    @Override
    public void setStuffLocation(BlnoOperateStuffLocationReqVO blnoOperateReqVO) {

        List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds(blnoOperateReqVO.getBlnoIds());
        if (!blnoDOList.isEmpty()) {
            blnoVslvoyMapper.update(null, new LambdaUpdateWrapper<BlnoVslvoyDO>()
                    .in(BlnoVslvoyDO::getBlnoId, blnoOperateReqVO.getBlnoIds())
                    .in(BlnoVslvoyDO::getPodCode, blnoDOList.get(0).getPodCode())
                    .set(BlnoVslvoyDO::getStuffLocIm, blnoOperateReqVO.getStuffLocation()));

            saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_TWENTY.getValue(),
                    SiOperateTypeEnum.OPERATE_SI_TWENTY.getLabel(), "");
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setDoPart(List<BlnoOperateDoPartReqVO> blnoOperateReqVos) {
        UserProfileDTO checkedData = nucApi.getUserProfile().getCheckedData();
        if (ObjectUtil.isEmpty(checkedData)) {
            throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.STATUS_USER_NOT_FOUND);
        }
        List<DoInfoDO> blnoInfos = new ArrayList<>();
        //todo 优化项 批量查
        blnoOperateReqVos.forEach(vo -> {
            DoInfoDO doInfoDO = doInfoMapper.selectOne(new LambdaQueryWrapper<DoInfoDO>()
                    .eq(DoInfoDO::getBlnoId, vo.getBlnoId()).last("limit 1"));
            if (doInfoDO == null) {
                doInfoDO = DoInfoDO.builder()
                        .blnoId(vo.getBlnoId())
                        .doStatus("")
                        .doExchangerCode(vo.getDoExchangerCode())
                        .doType(vo.getDoType())
                        .setDoTime(LocalDateTime.now())
                        .setDoOperator(checkedData.getId().toString())
                        .doExchangerNameEn(vo.getDoExchangerNameEn())
                        .doExchangerNameCn(vo.getDoExchangerNameCn())
                        .uniqueIdentifier(vo.getTaxId())
                        .build();
            } else {
                doInfoDO.setDoExchangerCode(vo.getDoExchangerCode());
                doInfoDO.setDoType(vo.getDoType());
                doInfoDO.setSetDoTime(LocalDateTime.now());
                doInfoDO.setSetDoOperator(checkedData.getId().toString());
                doInfoDO.setDoExchangerNameEn(vo.getDoExchangerNameEn());
                doInfoDO.setDoExchangerNameCn(vo.getDoExchangerNameCn());
                doInfoDO.setUniqueIdentifier(vo.getTaxId());
            }
            blnoInfos.add(doInfoDO);
        });
        doInfoMapper.saveOrUpdateBatch(blnoInfos);

        saveOperateLogBatch(blnoOperateReqVos.stream().map(BlnoOperateDoPartReqVO::getBlnoId).collect(Collectors.toList()), SI_OPERATE_TYPE, SiOperateTypeEnum.OPERATE_SI_TWENTY_ONE.getValue(),
                SiOperateTypeEnum.OPERATE_SI_TWENTY_ONE.getLabel(), "");
    }

    @Override
    public String printDocument(HttpServletResponse response, BlnoOperatePrintDocumentReqVO blnoOperateReqVO) {

        if (blnoOperateReqVO.getDocumentType().equals(DO)) {

            //只有设置了 D/O 类型的提单才能打印 D/O
            DoInfoDO doInfoDO = doInfoMapper.selectOne(new LambdaQueryWrapper<DoInfoDO>()
                    .eq(DoInfoDO::getBlnoId, blnoOperateReqVO.getBlnoId())
                    .last("limit 1"));

            if (doInfoDO != null) {
                if (StringUtils.isBlank(doInfoDO.getDoType())) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.PRINT_DO_ERROR_NO_DO_TYPE);
                }
            }
            //todo 暂不实现
            throw ServiceExceptionUtil.exception(TblisustampFilesErrorCodeConstants.ILLEGAL_STATUS);
        }

        //交货记录  DELIVERY NOTE
        if (blnoOperateReqVO.getDocumentType().equals(DN)) {
            //todo 暂不实现
            throw ServiceExceptionUtil.exception(TblisustampFilesErrorCodeConstants.ILLEGAL_STATUS);
        }
        return null;
    }

//    @Override
//    public void setSendArriveNotice(BlnoOperateDoReqVO blnoOperateReqVO) {
//
//        List<BlnoDO> blnoDOList = blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>()
//                .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
//                .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()));
//        if (!blnoDOList.isEmpty()) {
//            setSendArriveNotice(blnoDOList);
//        }
//    }

    @Override
    public List<BlnoDoPartRespVO> getDoPart(List<Long> blnoIds) {
        return Collections.emptyList();
    }

    @Override
    public void setVslvoyTerminal(List<BlnoVslvoyUpdateReqVO> blnoVslvoyList,String imExType) {
        if (CollectionUtil.isNotEmpty(blnoVslvoyList)) {
            blnoVslvoyList.forEach(e -> {
                List<String> vesselVoys = new ArrayList<>();
                vesselVoys.add(e.getVesselCode());
                if(TransmodeEnum.LINER.getValue().equals(e.getTransmode())){
                    Map<String, String> terminalMap = costChargeUtil.getVoyPortTerminalMap(e.getLaneCodeEx(), e.getVoyageEx(), e.getVesselCode());
                    if (terminalMap != null) {
                        e.setPolTerminal(terminalMap.get(e.getPolCode()));
                        e.setPodTerminal(terminalMap.get(e.getPodCode()));
                    }
                }else{
                    BargePolPodReqDTO bargePolPodReqDto = new BargePolPodReqDTO();
                    bargePolPodReqDto.setBargeType(TransmodeEnum.getBargeTypeByValue(e.getTransmode()));
                    bargePolPodReqDto.setDateType(DATA_TYPE);
                    bargePolPodReqDto.setPol(e.getPolCode());
                    bargePolPodReqDto.setPod(e.getPodCode());
                    if (ExImTypeEnum.EX.getValue().equals(imExType)) {
                        bargePolPodReqDto.setPolExVoy(e.getVoyageEx());
                    } else {
                        bargePolPodReqDto.setPolExVoy(e.getVoyageIm());
                    }
                    List<SoVslvoyScheduleVO> soVslvoyScheduleVoList = voyScheduleService.multiLineScheduleByPolPod(bargePolPodReqDto);
                    if (CollectionUtils.isNotEmpty(soVslvoyScheduleVoList)) {
                        SoVslvoyScheduleVO scheduleVo = soVslvoyScheduleVoList.get(0);
                        BeanUtil.copyProperties(scheduleVo,e);
                    }
                }
            });
        }
    }

    @Override
    public List<BlnoDO> getBlNoDoListByBlNo(List<String> blNos) {
        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoDO::getBlNo, blNos);
        queryWrapperX.eq(BlnoDO::getValid, SiValidEnum.VALID.getValue());
        List<BlnoDO> blnoDos = blnoMapper.selectList(queryWrapperX);
        return blnoDos;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createSiChange(BlnoInfoVO createReqVO) {
        BlnoBaseInfoVO blnoBasInfoVO = createReqVO.getBlnoBasInfoVO();
        this.checkSiChange(blnoBasInfoVO.getId());
        BlnoInfoVO temporarySi = judgeNoticeType(createReqVO);
        this.commitSi(temporarySi);
    }

    private BlnoInfoVO judgeNoticeType(BlnoInfoVO newSiInfo) {
        BlnoInfoVO oldSiInfo = this.getDetail(newSiInfo.getBlnoBasInfoVO().getId());
        BlnoInfoVO blnoInfoVO = compareSiChange(newSiInfo, oldSiInfo);
        this.dealChangeBlnoInfo(blnoInfoVO);
        return blnoInfoVO;
    }

    private void dealChangeBlnoInfo(BlnoInfoVO blnoInfoVO) {
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        // 旧版本的提单 si 更改状态修改
        blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                .eq(BlnoDO::getId, blnoBasInfoVO.getId())
                .set(BlnoDO::getSiChangeStatus, SI_AMENDMENT_PROCESSING.getValue()));
        // 待提交的提单 主信息
        blnoBasInfoVO.setOldBlnoId(blnoBasInfoVO.getId());
        blnoBasInfoVO.setId(null);
        blnoBasInfoVO.setSiChangeStatus(null);
        blnoBasInfoVO.setValid(SiValidEnum.PENDING.getValue());

        blnoInfoVO.getBlnoVslvoys().forEach(n -> {
            n.setId(null);
            n.setBlnoId(null);
        });
        blnoInfoVO.getBlnoScns().forEach(n -> {
            n.setId(null);
            n.setBlnoId(null);
        });
        blnoInfoVO.getBlnoCargos().forEach(n -> {
            n.setId(null);
            n.setBlnoId(null);
        });
        blnoInfoVO.getBlnoCntrs().forEach(n -> {
            n.setId(null);
            n.setBlnoId(null);
        });
        blnoInfoVO.getBlnoPayments().forEach(n -> {
            n.setId(null);
            n.setBlnoId(null);
        });
        blnoInfoVO.getAttachmentReqVO().forEach(n -> {
            n.setId(null);
            n.setReferenceId(null);
        });
        blnoInfoVO.getReqVOList().forEach(n -> {
            n.setId(null);
            n.setReferenceId(null);
        });
    }

    private BlnoInfoVO compareSiChange(BlnoInfoVO newSiInfo, BlnoInfoVO oldSiInfo) {
        String noticeType = "";
        Boolean isLockVslvoy = costChargeUtil.checkLockVoyage(BeanUtil.copyToList(oldSiInfo.getBlnoVslvoys(), BlnoVslvoyDO.class));
        noticeType = assertNoticeType(newSiInfo, oldSiInfo);
        if (!isLockVslvoy) {
            noticeType = ConfirmNoticeEnum.SIC.getValue();
        }
        oldSiInfo.getBlnoBasInfoVO().setNoticeType(noticeType);
        return oldSiInfo;
    }

    private String assertNoticeType(BlnoInfoVO newSiInfo, BlnoInfoVO oldSiInfo) {
        List<BlnoVslvoyChangeVO> changeVslvoyVos = BeanUtil.copyToList(newSiInfo.getBlnoVslvoys(), BlnoVslvoyChangeVO.class);
        List<BlnoScnChangeVO> changeScnVos = BeanUtil.copyToList(newSiInfo.getBlnoScns(), BlnoScnChangeVO.class);
        List<BlnoCargoChangeVO> changeCargoVos = BeanUtil.copyToList(newSiInfo.getBlnoCargos(), BlnoCargoChangeVO.class);
        List<BlnoCntrChangeVO> changeCntrVos = BeanUtil.copyToList(newSiInfo.getBlnoCntrs(), BlnoCntrChangeVO.class);
        BlnoChangeChildVO blnoChangeChildVO = BeanUtil.copyProperties(newSiInfo.getBlnoBasInfoVO(), BlnoChangeChildVO.class);

        Boolean mainInfoEqual = blnoChangeChildVO.equals(BeanUtil.copyProperties(oldSiInfo.getBlnoBasInfoVO(), BlnoChangeChildVO.class));
        Boolean vslvoyInfoEqual = CollectionUtils.isEqualCollection(changeVslvoyVos, BeanUtil.copyToList(oldSiInfo.getBlnoVslvoys(), BlnoVslvoyChangeVO.class));
        Boolean scnInfoEqual = CollectionUtils.isEqualCollection(changeScnVos, BeanUtil.copyToList(oldSiInfo.getBlnoScns(), BlnoScnChangeVO.class));
        Boolean cargoInfoEqual = CollectionUtils.isEqualCollection(changeCargoVos, BeanUtil.copyToList(oldSiInfo.getBlnoCargos(), BlnoCargoChangeVO.class));
        Boolean cntrInfoEqual = CollectionUtils.isEqualCollection(changeCntrVos, BeanUtil.copyToList(oldSiInfo.getBlnoCntrs(), BlnoCntrChangeVO.class));
        // 如果没有任何信息修改 修改申请驳回
        if (mainInfoEqual && vslvoyInfoEqual && scnInfoEqual && cargoInfoEqual && cntrInfoEqual) {
            throw ServiceExceptionUtil.exception(ConfirmNoticeErrorCodeConstants.CONFIRM_CHANGE_NOT_CONTENT);
        }

        // 判断标准：如果改了 routeid 或者船名航次，不管是否通知改了其他信息都是 COD 类型。
        // 如果只改了除 routeid 和船名航次以外的信息则是 MCN 类型
        BlnoChangeVO blnoChangeVO = BeanUtil.copyProperties(newSiInfo.getBlnoBasInfoVO(), BlnoChangeVO.class);
        String noticeType = blnoChangeVO.equals(BeanUtil.copyProperties(oldSiInfo.getBlnoBasInfoVO(), BlnoChangeVO.class))
                ? ConfirmNoticeEnum.MCN.getValue() : ConfirmNoticeEnum.COD.getValue();

        // 防止rpc 请求提交 页面不可修改的字段 处理
        Map<Long, BlnoScnUpdateReqVO> newScnMap = newSiInfo.getBlnoScns().stream().collect(Collectors.toMap(BlnoScnUpdateReqVO::getId, e -> e, (o1, o2) -> o1));
        Map<Long, BlnoCntrUpdateReqVO> newCntrMap = newSiInfo.getBlnoCntrs().stream().collect(Collectors.toMap(BlnoCntrUpdateReqVO::getId, e -> e, (o1, o2) -> o1));
        oldSiInfo.getBlnoScns().forEach(n -> {
            BlnoScnUpdateReqVO blnoScnUpdateReqVO = newScnMap.get(n.getId());
            if (blnoScnUpdateReqVO != null) {
                BeanUtil.copyProperties(blnoScnUpdateReqVO, n);
            }
        });
        Iterator<BlnoScnUpdateReqVO> scnIterator = oldSiInfo.getBlnoScns().iterator();
        while (scnIterator.hasNext()) {
            BlnoScnUpdateReqVO n = scnIterator.next();
            BlnoScnUpdateReqVO blnoScnUpdateReqVO = newScnMap.get(n.getId());
            if (blnoScnUpdateReqVO != null) {
                BeanUtil.copyProperties(blnoScnUpdateReqVO, n);
            } else {
                scnIterator.remove();
            }
        }
        newSiInfo.getBlnoScns().stream().filter(n -> ObjectUtil.isNull(n.getId())).forEach(n -> oldSiInfo.getBlnoScns().add(n));

        oldSiInfo.getBlnoCntrs().forEach(n -> {
            BlnoCntrUpdateReqVO blnoCntrUpdateReqVO = newCntrMap.get(n.getId());
            if (blnoCntrUpdateReqVO != null) {
                BeanUtil.copyProperties(blnoCntrUpdateReqVO, n);
            }
        });
        Iterator<BlnoCntrUpdateReqVO> cntrIterator = oldSiInfo.getBlnoCntrs().iterator();
        while (cntrIterator.hasNext()) {
            BlnoCntrUpdateReqVO n = cntrIterator.next();
            BlnoCntrUpdateReqVO blnoCntrUpdateReqVO = newCntrMap.get(n.getId());
            if (blnoCntrUpdateReqVO != null) {
                BeanUtil.copyProperties(blnoCntrUpdateReqVO, n);
            } else {
                cntrIterator.remove();
            }
        }
        newSiInfo.getBlnoCntrs().stream().filter(n -> ObjectUtil.isNull(n.getId())).forEach(n -> oldSiInfo.getBlnoCntrs().add(n));

        BeanUtil.copyProperties(blnoChangeChildVO, oldSiInfo.getBlnoBasInfoVO());
        BlnoCargoUpdateReqVO blnoCargoUpdateReqVO = oldSiInfo.getBlnoCargos().get(0);
        BlnoCargoUpdateReqVO blnoCargoChangeVO = newSiInfo.getBlnoCargos().get(0);
        BeanUtil.copyProperties(blnoCargoChangeVO, blnoCargoUpdateReqVO);

        oldSiInfo.setBlnoVslvoys(newSiInfo.getBlnoVslvoys());
        return noticeType;
    }

    @Override
    public Boolean checkSiChange(Long id) {
        BlnoDO blnoDO = blnoMapper.selectById(id);
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        if (NORMAL_SPLIT.getValue().equals(blnoDO.getSplitCombineType())
                || NORMAL_COMBINE.getValue().equals(blnoDO.getSplitCombineType())
                || COMBINE_NORMAL.getValue().equals(blnoDO.getSplitCombineType())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_SC_NOT_CHANGE);
        }
        Boolean flag = !Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue()
                , SiStatusEnum.AwaitingMaterialUploaded.getValue()
                , SiStatusEnum.BookingConfirmed.getValue()).contains(blnoDO.getSiStatus());
        if (flag) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_ERROR_SI_STATUS);
        }
        Long count = blnoMapper.selectCount(new LambdaQueryWrapperX<BlnoDO>()
                .eq(BlnoDO::getValid, SiValidEnum.PENDING.getValue())
                .eq(BlnoDO::getBlNo, blnoDO.getBlNo()));
        if (count != 0) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_ERROR_NOTICE_EXISTED);
        }
        return true;
    }


    private void lclCheck(BlnoInfoVO blnoInfoVO, List<BlnoCheckSiErrorPrompt> checkSiErrorPrompts,
                          AtomicReference<Boolean> basicCheckFlag) {
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        List<BlnoPaymentUpdateReqVO> blnoPayments = blnoInfoVO.getBlnoPayments();
        List<BlnoVslvoyUpdateReqVO> blnoVslvoys = blnoInfoVO.getBlnoVslvoys();
        List<BlnoScnUpdateReqVO> blnoScns = blnoInfoVO.getBlnoScns();
        List<BlnoCntrUpdateReqVO> blnoCntrs = blnoInfoVO.getBlnoCntrs();
        List<BlnoDO> blnoDos = blnoMapper
                .selectList(new LambdaQueryWrapper<BlnoDO>().eq(BlnoDO::getBkgVesselCode, blnoBasInfoVO.getBkgVesselCode())
                        .eq(BlnoDO::getBkgVoyage, blnoBasInfoVO.getBkgVoyage()).ne(BlnoDO::getId, blnoBasInfoVO.getId())
                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                        .ne(BlnoDO::getSiStatus, SiStatusEnum.BookingDraft.getValue()).eq(BlnoDO::getPodBlFlag, true));
        if (CollectionUtil.isNotEmpty(blnoDos)) {
            List<Long> blnoIds = blnoDos.stream().map(BlnoDO::getId).collect(Collectors.toList());
            List<String> cntrNos = blnoCntrs.stream().map(BlnoCntrUpdateReqVO::getCntrNo).collect(Collectors.toList());
            List<BlnoCntrDO> blnoCntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapper<BlnoCntrDO>()
                    .in(BlnoCntrDO::getBlnoId, blnoIds).in(BlnoCntrDO::getCntrNo, cntrNos));
            if (CollectionUtil.isNotEmpty(blnoCntrDos)) {
                // 检测信息
                Map<String, BlnoCntrUpdateReqVO> checkCntrMap =
                        blnoCntrs.stream().collect(Collectors.toMap(n -> n.getCntrNo(), n -> n));
                List<BlnoVslvoySameVO> blnoVslvoySameVos = BeanUtil.copyToList(blnoVslvoys, BlnoVslvoySameVO.class);
                List<BlnoScnSameVO> blnoScnSameVos = BeanUtil.copyToList(blnoScns, BlnoScnSameVO.class);

                // 待比较信息
                List<Long> selectBlnoIds = blnoCntrDos.stream().map(BlnoCntrDO::getBlnoId).collect(Collectors.toList());
                List<BlnoDO> beBlnoDos =
                        blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>().in(BlnoDO::getId, selectBlnoIds).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
                List<BlnoVslvoyDO> beBlnoVslvoyDos = blnoVslvoyMapper
                        .selectList(new LambdaQueryWrapperX<BlnoVslvoyDO>().in(BlnoVslvoyDO::getBlnoId, selectBlnoIds));
                List<BlnoScnDO> beBlnoScnDos = blnoScnMapper
                        .selectList(new LambdaQueryWrapperX<BlnoScnDO>().in(BlnoScnDO::getBlnoId, selectBlnoIds));
                List<BlnoPaymentDO> blnoPaymentDos =
                        blnoPaymentMapper.selectList(new LambdaQueryWrapperX<BlnoPaymentDO>()
                                .in(BlnoPaymentDO::getBlnoId, selectBlnoIds).eq(BlnoPaymentDO::getChargeCode, CHARGE_CODE));
                Map<Long, List<BlnoCntrDO>> cntrMap =
                        blnoCntrDos.stream().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));
                Map<Long, BlnoDO> blnoDoMap = beBlnoDos.stream().collect(Collectors.toMap(n -> n.getId(), n -> n));
                Map<Long, List<BlnoVslvoyDO>> vslvoyMap =
                        beBlnoVslvoyDos.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
                Map<Long, List<BlnoScnDO>> scnMap =
                        beBlnoScnDos.stream().collect(Collectors.groupingBy(BlnoScnDO::getBlnoId));
                Map<Long, BlnoPaymentDO> paymentMap =
                        blnoPaymentDos.stream().collect(Collectors.toMap(n -> n.getBlnoId(), n -> n));
                // 拼箱判断标准：相同的船名航次下面不同的提单号下面有相同的箱号，那么这个箱号就是拼箱。
                // 拿不同 满足条件 的提单信息 和待 check 的 si 一一 比较 如果有任何一个不满足 则阻断
                for (Long blnoId : blnoDoMap.keySet()) {
                    // 主信息对比
                    BlnoDO blnoDO = blnoDoMap.get(blnoId);
                    if (!StringUtils.equals(blnoDO.getSpaceOwner(), blnoBasInfoVO.getSpaceOwner())
                            || !StringUtils.equals(blnoDO.getCntrOwner(), blnoBasInfoVO.getCntrOwner())
                            || !StringUtils.equals(blnoDO.getCntrStatus(), blnoBasInfoVO.getCntrStatus())
                            || !StringUtils.equals(blnoDO.getShippingTerm(), blnoBasInfoVO.getShippingTerm())
                            || !StringUtils.equals(blnoDO.getSignOrder(), blnoBasInfoVO.getSignOrder())) {
                        checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SEVEN.getPromptCode(),
                                CHECK_PROMPT_SEVEN.getPromptDesc(), CHECK_PROMPT_SEVEN.getPromptType()));
                        basicCheckFlag.set(false);
                        break;
                    }
                    //收发通
                    List<BlnoScnDO> blnoScnDos = scnMap.get(blnoId);
                    BlnoScnUpdateReqVO blnoScnUpdateReqVO = blnoScns.stream().filter(n -> PartyTypeCodeEnum.CONSIGNEE.getValue().equals(n.getPartyTypeCode())).findFirst().orElse(null);
                    BlnoScnDO blnoScnDO = blnoScnDos.stream().filter(n -> PartyTypeCodeEnum.CONSIGNEE.getValue().equals(n.getPartyTypeCode())).findFirst().orElse(null);
                    if (blnoScnUpdateReqVO != null && blnoScnDO != null) {
                        if (!StringUtils.equals(blnoScnUpdateReqVO.getCompanyName(), blnoScnDO.getCompanyName())) {
                            checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SEVEN.getPromptCode(),
                                    CHECK_PROMPT_SEVEN.getPromptDesc(), CHECK_PROMPT_SEVEN.getPromptType()));
                            basicCheckFlag.set(false);
                            break;
                        }
                    }
                    // 箱信息对比
                    List<BlnoCntrDO> blnoCntr = cntrMap.get(blnoId);
                    blnoCntr.stream().anyMatch(n -> {
                        if (!StringUtils.equals(n.getCntrSize(), checkCntrMap.get(n.getCntrNo()).getCntrSize())
                                || !StringUtils.equals(n.getCntrType(), checkCntrMap.get(n.getCntrNo()).getCntrType())) {
                            checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SEVEN.getPromptCode(),
                                    CHECK_PROMPT_SEVEN.getPromptDesc(), CHECK_PROMPT_SEVEN.getPromptType()));
                            basicCheckFlag.set(false);
                        }
                        return false;
                    });
                    // 船期信息
                    List<BlnoVslvoySameVO> compareVslvoySameVos =
                            BeanUtil.copyToList(vslvoyMap.get(blnoId), BlnoVslvoySameVO.class);
                    if (!CollectionUtils.isEqualCollection(blnoVslvoySameVos, compareVslvoySameVos)) {
                        checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SEVEN.getPromptCode(),
                                CHECK_PROMPT_SEVEN.getPromptDesc(), CHECK_PROMPT_SEVEN.getPromptType()));
                        basicCheckFlag.set(false);
                        break;
                    }
                    // 指定支付方式 信息
                    BlnoPaymentDO blnoPaymentDO = paymentMap.get(blnoId);
                    BlnoPaymentUpdateReqVO blnoPaymentUpdateReqVO =
                            blnoPayments.stream().filter(n -> n.getChargeCode().equals(CHARGE_CODE)).findAny().get();
                    if (!StringUtils.equals(blnoPaymentDO.getDesignatedPayment(),
                            blnoPaymentUpdateReqVO.getDesignatedPayment())
                            && !StringUtils.equals(blnoPaymentDO.getDefaultPayment(),
                            blnoPaymentUpdateReqVO.getDefaultPayment())) {
                        checkSiErrorPrompts.add(new BlnoCheckSiErrorPrompt(CHECK_PROMPT_SEVEN.getPromptCode(),
                                CHECK_PROMPT_SEVEN.getPromptDesc(), CHECK_PROMPT_SEVEN.getPromptType()));
                        basicCheckFlag.set(false);
                        break;
                    }
                }
            }
        }
    }

    private boolean checkVgm(BlnoCntrUpdateReqVO blnoCntrUpdateReqVO) {
        // 1.1（WEIGHT+TARE）-VGM<VGM *5% ，且（WEIGHT+TARE）-VGM<1000KGS
        // 1.2. VGM-（WEIGHT+TARE）< VGM *5% ，且VGM-（WEIGHT+TARE）<1000KGS
        // 满足以上之一就可以
        if (ObjectUtil.isNotNull(blnoCntrUpdateReqVO.getVgmWeight())) {
            BigDecimal weight = blnoCntrUpdateReqVO.getGrossWeight();
            BigDecimal vgmWeight = blnoCntrUpdateReqVO.getVgmWeight();
            BigDecimal cntrTare = blnoCntrUpdateReqVO.getCntrTare();
            BigDecimal compute = (weight.add(cntrTare).subtract(vgmWeight)).abs();
            boolean flag = (compute).compareTo(vgmWeight.multiply(new BigDecimal(0.05))) == -1
                    && (compute).compareTo(new BigDecimal(1000)) == -1;
            return flag;
        } else {
            return true;
        }
    }

    private boolean isValidString(String input) {
        if (input == null) {
            return false;
        }
        byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
        return bytes.length <= 256;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public BlnoCheckSiRespVO confirmSi(BlnoCheckSiReqVO blnoCheckSiReqVO) {
        BlnoCheckSiRespVO blnoCheckSiRespVo = this.checkSi(blnoCheckSiReqVO);
        blnoCheckSiRespVo.setConfirmSuccessFlag(blnoCheckSiRespVo.getCheckSuccessFlag());
        if (blnoCheckSiRespVo.getCheckSuccessFlag()) {
            BlnoBaseInfoVO blnoBasInfoVO = blnoCheckSiReqVO.getBlnoInfoVO().getBlnoBasInfoVO();
            LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
            lambdaUpdate.eq(BlnoDO::getId, blnoBasInfoVO.getId()).set(BlnoDO::getSiStatus,
                    SiStatusEnum.BookingPendingSpace.getValue());
            blnoMapper.update(null, lambdaUpdate);
            saveOperateLog(blnoBasInfoVO, SI_OPERATE_TYPE, CONFIRM_SI.getValue(), CONFIRM_SI.getLabel());
            taskExecutor.execute(() -> {
                blnoBlPrintLogService.printTheDraft(blnoCheckSiReqVO.getResponse(), blnoBasInfoVO.getId());
            });
        }
        return blnoCheckSiRespVo;
    }

    @Override
    public List<BlnoLocaCoastExRespVO> getBlnoWithLocalCostList(BlnoLocalCostExReqVO reqVO) {

        return blnoMapper.getBlnoWithLocalCostList(reqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setIssuePlace(BlnoOperateReqVO blnoOperateReqVO) {
        if (StringUtils.isNotEmpty(blnoOperateReqVO.getSignType())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_PLACE_NOT_UPDATE);
        }
        volatileStatus(blnoOperateReqVO);
        if (StringUtils.isEmpty(blnoOperateReqVO.getIssuePlace())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NO_PLACE);
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getIssuePlace,
                        blnoOperateReqVO.getIssuePlace())
                .set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_ONE.getValue(),
                OPERATE_SI_ONE.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setIssueDate(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        if (ObjectUtil.isNull(blnoOperateReqVO.getIssueDate())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NO_DATE);
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getIssueDate,
                blnoOperateReqVO.getIssueDate()).set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_TWO.getValue(),
                OPERATE_SI_TWO.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void operateSignType(BlnoOperateReqVO blnoOperateReqVO) {
        List<BlnoDO> blnoDos = volatileStatus(blnoOperateReqVO);
        // 判断是否 有提单主体 有的话直接维护 没有不让签发
        dealBlEntity(blnoDos);
        if (StringUtils.isEmpty(blnoOperateReqVO.getSignType())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NO_SIGN_TYPE);
        }
        if (CommonConstants.T.equals(blnoOperateReqVO.getSignType()) || CommonConstants.W.equals(blnoOperateReqVO.getSignType())) {
            //如果是设置电放  发送通知
            confirmNoticeService.createConfirmNotice(blnoOperateReqVO.getBlnoIds(), ConfirmNoticeEnum.TELEX_SEAWAY.getValue()
                    , NoticeRespConstants.SETUP_TELEX_RELEASE, ConfirmNoticeOperateEnum.NOTICE.getValue());
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                .set(BlnoDO::getSignType, blnoOperateReqVO.getSignType())
                .set(BlnoDO::getUpdateTime, LocalDateTime.now())
                .set(BlnoDO::getSiStatus, SiStatusEnum.BookingRejected.getValue());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_THREE.getValue(),
                OPERATE_SI_THREE.getLabel(), blnoOperateReqVO.getRemark());
    }

    private void dealBlEntity(List<BlnoDO> blnoDos) {
        blnoDos.stream().forEach(n -> {
            // 如果获取不到提单主体  则不让签发
            List<BlnoVslvoyDO> vslvoyList = blnoVslvoyMapper.selectList(new LambdaQueryWrapperX<BlnoVslvoyDO>().eq(BlnoVslvoyDO::getBlnoId,n.getId()));
            BlnoVslvoyDO blnoVslvoyDo = vslvoyList.stream().filter(vs -> n.getBkgVesselCode()
                    .equals(vs.getVesselCode()) && n.getBkgVoyage().equals(vs.getVoyageEx())).findFirst().orElse(null);
            if(ObjectUtil.isNull(blnoVslvoyDo)){
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_FIRSR_LINE);
            }
            List<LaneRespVO> laneRespVos = null ;
            try {
                laneRespVos = laneService.selectLanesByLaneCodes(Arrays.asList(blnoVslvoyDo.getLaneCodeEx()));
                if(CollectionUtil.isEmpty(laneRespVos)){
                    throw ServiceExceptionUtil.exception(ApiErrorCodeConstants.QUERY_LANE_FAILED);
                }
            } catch (Exception e) {
                throw ServiceExceptionUtil.exception(ApiErrorCodeConstants.QUERY_LANE_FAILED);
            }
            LaneRespVO laneRespVO = laneRespVos.get(0);
            n.setBlType(laneRespVO.getBusinessEntity());
        });
        blnoMapper.updateBatch(blnoDos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setElgReceive(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        if (StringUtils.isEmpty(blnoOperateReqVO.getElgReceive())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NO_EL);
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getElgReceive,
                blnoOperateReqVO.getElgReceive()).set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_FOUR.getValue(),
                OPERATE_SI_FOUR.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setLgReceive(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        if (StringUtils.isEmpty(blnoOperateReqVO.getLgReceive())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NO_PL);
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getLgReceive,
                blnoOperateReqVO.getLgReceive()).set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_FIVE.getValue(),
                OPERATE_SI_FIVE.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void signRestriction(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        List<BlnoDO> blnoDos =
                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (n.getRestrictionFlag() == true) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_FLAG_ILLEGAL);
            }
            return false;
        });
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getRestrictionFlag, true)
                .set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_SIX.getValue(),
                OPERATE_SI_SIX.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    public void setBlLock(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        List<BlnoDO> blnoDos =
                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (n.getBlLockFlag() == true) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_INFO_LOCK_ERROR);
            }
            if (SiStatusEnum.BookingPendingSpace.getValue().equals(n.getSiStatus())
                    || SiStatusEnum.AwaitingMaterialUploaded.getValue().equals(n.getSiStatus())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SI_STATUS_ILLEGAL);
            }
            return false;
        });
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getBlLockFlag, true)
                .set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_ELEVEN.getValue(),
                OPERATE_SI_ELEVEN.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelRestriction(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        List<BlnoDO> blnoDos =
                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (n.getRestrictionFlag() == false) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_FLAG_ILLEGAL);
            }
            return false;
        });
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getRestrictionFlag, false).set(BlnoDO::getUpdateTime, LocalDateTime.now());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_SEVEN.getValue(),
                OPERATE_SI_SEVEN.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOriginalBl(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        List<BlnoDO> blnoDos =
                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (!CommonConstants.B.equals(n.getSignType())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SIGN_TYPE_ILLEGAL);
            }
            if (StringUtils.isNotBlank(n.getDoStatus())) {
                Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_IS_DO.getCode(), n.getBlNo());
            }
            return false;
        });
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getSignType, "")
                .set(BlnoDO::getUpdateTime, LocalDateTime.now())
                .set(BlnoDO::getSiStatus, SiStatusEnum.BookingConfirmed.getValue());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_EIGHT.getValue(),
                OPERATE_SI_EIGHT.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelTelex(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        List<BlnoDO> blnoDos =
                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (!CommonConstants.T.equals(n.getSignType()) && !CommonConstants.W.equals(n.getSignType())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SIGN_TYPE_ILLEGAL);
            }
            if (StringUtils.isNotBlank(n.getDoStatus())) {
                Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_IS_DO.getCode(), n.getBlNo());
            }
            return false;
        });
        List<Long> blnoIds = blnoDos.stream().map(BlnoDO::getId).collect(Collectors.toList());
        Long count = confirmNoticeMapper.selectCount(new LambdaQueryWrapperX<ConfirmNoticeDO>()
                .in(ConfirmNoticeDO::getBlnoId, blnoIds)
                .eq(ConfirmNoticeDO::getStatus, ConfirmNoticeStatusEnum.PENDING.getValue())
                .eq(ConfirmNoticeDO::getNotice, CANCEL_TELEX_SEAWAY.getValue()));
        if (count != 0L) {
            throw ServiceExceptionUtil.exception(ConfirmNoticeErrorCodeConstants.CONFIRM_DIAN_FANG_EXISTED);
        }
        //如果是取消电放  发送通知
        confirmNoticeService.createConfirmNotice(blnoOperateReqVO.getBlnoIds(), ConfirmNoticeEnum.CANCEL_TELEX_SEAWAY.getValue()
                , NoticeRespConstants.CANCEL_TELEX_SEAWAY, ConfirmNoticeOperateEnum.APPROVAL.getValue());
    }

//    @Transactional(rollbackFor = Exception.class)
//    @Override
//    public void cancelTelex(BlnoOperateIdsReqVO blnoOperateReqVO) {
//        List<BlnoDO> blnoDos =
//                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
//        blnoDos.stream().anyMatch(n -> {
//            if (StringUtils.isNotBlank(n.getDoStatus())) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_IS_DO,n.getBlNo());
//            }
//            if (!CommonConstants.T.equals(n.getSignType()) && !CommonConstants.W.equals(n.getSignType())) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_IS_NOT_TELEX_SEAWAY,n.getBlNo());
//            }
//            return false;
//        });
//        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
//        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getSignType, null);
//        blnoMapper.update(null, lambdaUpdate);
//        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_NINE.getValue(),
//                OPERATE_SI_NINE.getLabel(), "");
//    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelElectronicBl(BlnoOperateReqVO blnoOperateReqVO) {
        volatileStatus(blnoOperateReqVO);
        List<BlnoDO> blnoDos =
                blnoMapper.selectList(new LambdaQueryWrapper<BlnoDO>().in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (!CommonConstants.E.equals(n.getSignType())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SIGN_TYPE_ILLEGAL);
            }
            if (StringUtils.isNotBlank(n.getDoStatus())) {
                Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.SI_IS_DO.getCode(), n.getBlNo());
            }
            return false;
        });
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate.in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds()).set(BlnoDO::getSignType, "")
                .set(BlnoDO::getUpdateTime, LocalDateTime.now())
                .set(BlnoDO::getSiStatus, SiStatusEnum.BookingConfirmed.getValue());
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_TEN.getValue(),
                OPERATE_SI_TEN.getLabel(), blnoOperateReqVO.getRemark());
    }

    @Override
    public CntrBaseInfoVO getContainInfo(String containNo) {
        List<CntrBaseInfoDTO> checkedData;
        try {
            CommonResult<List<CntrBaseInfoDTO>> listCommonResult = cntrInfoApi.queryCntrInfo(Arrays.asList(containNo));
            checkedData = listCommonResult.getCheckedData();
        } catch (Exception e) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.EQU_REQUEST_FAILURE);
        }
        if (CollectionUtil.isNotEmpty(checkedData)) {
            CntrBaseInfoVO cntrBaseInfoVO = BeanUtil.copyProperties(checkedData.get(0), CntrBaseInfoVO.class);
            if (StringUtils.isNotEmpty(cntrBaseInfoVO.getCarrier()) && cntrBaseInfoVO.getCarrier().length() > CommonConstants.THREE) {
                cntrBaseInfoVO.setCarrier(cntrBaseInfoVO.getCarrier().substring(0, CommonConstants.THREE));
            }
            return cntrBaseInfoVO;
        } else {
            return null;
        }
    }

    @Override
    public List<String> getBlContainDepositStatus(@Valid BlnoOperateIdsReqVO blnoOperateIdsReqVO) {

        Locale currentLocale = LocaleContextHolder.getLocale();
        //todo 箱管接口
//        List<String> depositHasBeenCollectedNameBlnoList = Arrays.asList("押金公司名称+提单号1", "押金公司名称+提单号2");
        List<String> depositHasBeenCollectedNameBlnoList = new ArrayList<>();
        List<ImReleaseStatusDTO> checkedData;
        List<BlnoDO> blnoDOList = blnoMapper
                .selectList(new LambdaQueryWrapper<BlnoDO>()
                        .in(BlnoDO::getId, blnoOperateIdsReqVO.getBlnoIds())
                );
        List<String> containNo = blnoDOList
                .stream().map(BlnoDO::getBlNo).collect(Collectors.toList());
        ImReleaseStatusReqParam imReleaseStatusReqParam = new ImReleaseStatusReqParam();
        imReleaseStatusReqParam.setBlnoList(containNo);
        imReleaseStatusReqParam.setPortCode(blnoOperateIdsReqVO.getCurrentPort());
        try {
            CommonResult<List<ImReleaseStatusDTO>> listCommonResult = eirReleaseApi.getImReleaseStatus(imReleaseStatusReqParam);
            checkedData = listCommonResult.getCheckedData();
        } catch (Exception e) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.EQU_REQUEST_FAILURE);
        }
        if (CollectionUtil.isNotEmpty(checkedData)) {
            for (ImReleaseStatusDTO checkedDatum : checkedData) {

                if (checkedDatum.getReleaseStatus()) {
                    //todo 国际化
                    String msg = checkedDatum.getClientName() + checkedDatum.getBlno() + "已收" + i18nsUtil.getMessage(VgmErrorCodeConstants.TYPE_CODE_NOT_EXISTS.getCode(), currentLocale);
                    depositHasBeenCollectedNameBlnoList.add(checkedDatum.getClientName() + checkedDatum.getBlno() + "是否继续D/O？");
                }
            }
        }
        return depositHasBeenCollectedNameBlnoList;
    }


    private Boolean checkContainNo(List<BlnoCntrUpdateReqVO> blnoCntrs) {
        List<String> containNos = blnoCntrs.stream().map(BlnoCntrUpdateReqVO::getCntrNo).collect(Collectors.toList());
        List<CntrBaseInfoDTO> checkedData = new ArrayList<>();
        try {
            CommonResult<List<CntrBaseInfoDTO>> listCommonResult = cntrInfoApi.queryCntrInfo(containNos);
            checkedData = listCommonResult.getCheckedData();
        } catch (Exception e) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.EQU_REQUEST_FAILURE);
        }
        Map<String, BlnoCntrUpdateReqVO> cntrUpdateReqVoMap =
                blnoCntrs.stream().collect(Collectors.toMap(BlnoCntrUpdateReqVO::getCntrNo, n -> n));
        checkedData.forEach(element -> {
            BlnoCntrUpdateReqVO blnoCntrUpdateReqVO = cntrUpdateReqVoMap.get(element.getCntrNo());
            if (blnoCntrUpdateReqVO != null) {
                if (!(blnoCntrUpdateReqVO.getCntrType().equals(element.getCntrType())
                        && blnoCntrUpdateReqVO.getCntrSize().equals(element.getCntrSize()))) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.CONTAINNO_TYPE_NOT_EXIST);
                }
            }
        });

        return true;
    }

    @Override
    public BlnoDO getBlno(BlnoDetailReqDto blnoDetailReqDto) {
        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eqIfPresent(BlnoDO::getId, blnoDetailReqDto.getBlnoId())
                .eqIfPresent(BlnoDO::getBlNo, blnoDetailReqDto.getBlNo())
                .eqIfPresent(BlnoDO::getBookingNo, blnoDetailReqDto.getBookingNo())
                .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                .eq(BlnoDO::getPodBlFlag,true)
                .last("limit 1");
        return blnoMapper.selectOne(queryWrapperX);

    }

    /**
     * 提单根据状态数量统计（参数：网厅的用户id,返回各个状态及各个状态下的提单数量合计, 例如：全部100，待审核：10，已确认10，待确认Draft10....已签发12）
     */
    @Override
    public BlnoStatisticsRespDto getBlnoStatistics(String csmCode,String creator) {

        BlnoStatisticsRespDto blnoStatisticsRespDto = new BlnoStatisticsRespDto();
        Map<String, Long> statusStatistics = new HashMap<>(16);
        blnoStatisticsRespDto.setStatusStatistics(statusStatistics);

        List<String> siStatusList = Arrays.stream(SiStatusEnum.values())
                .map(SiStatusEnum::getValue)
                .collect(Collectors.toList());

        LambdaQueryWrapperX<BlnoDO> wrapper = new LambdaQueryWrapperX<BlnoDO>()
                .eqIfPresent(BlnoDO::getBkpCode, csmCode)
                .eqIfPresent(BlnoDO::getPodBlFlag, Boolean.TRUE)
                .eqIfPresent(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                .eqIfPresent(BlnoDO::getCreator,creator)
                .inIfPresent(BlnoDO::getSiStatus, siStatusList);
        List<BlnoDO> blnoList = blnoMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(blnoList)) {
            return blnoStatisticsRespDto;
        }
        Map<String, Long> tempStats = blnoList.stream().collect(Collectors.groupingBy(BlnoDO::getSiStatus, Collectors.counting()));

        long all = 0L;
        for (SiStatusEnum siStatusEnum : SiStatusEnum.values()) {
            Long count = tempStats.getOrDefault(siStatusEnum.getValue(), 0L);
            statusStatistics.put(siStatusEnum.getValue(), count);
            all += count;
        }
        //SIO6状态 包括准备好签发，已签发的状态
        //已签发状态
        /*if (statusStatistics.get(SiStatusEnum.BookingRejected.getValue()) != 0) {
            List<BlnoDO> signTypeBlno = blnoList.stream().filter(e -> Objects.equals(SiStatusEnum.BookingRejected.getValue(), e.getSiStatus()) && StringUtils.isNotEmpty(e.getSignType())).collect(Collectors.toList());
            Long signCount = Long.valueOf(signTypeBlno.size());
            Long signingCount = statusStatistics.get(SiStatusEnum.BookingRejected.getValue());
            statusStatistics.replace(SiStatusEnum.BookingRejected.getValue(), signingCount - signCount);
            statusStatistics.put("SI07", Long.valueOf(signTypeBlno.size()));
        } else {
            statusStatistics.put("SI07", 0L);
        }*/
       /* Long all = 0L;
        // blno 表 根据sistauts group
        for (SiStatusEnum siStatusEnum : SiStatusEnum.values()) {
            Long count = blnoMapper.selectCount(new LambdaQueryWrapper<BlnoDO>().eq(BlnoDO::getSiStatus, siStatusEnum.getValue())
                            .eq(BlnoDO::getCreator, creator).eq(BlnoDO::getDatasource, dataSource));

            statusStatistics.put(siStatusEnum.getValue(), count);
            all += count;
        }*/
        //SIO6状态 包括准备好签发，已签发的状态

        statusStatistics.put("ALL", all);
        blnoStatisticsRespDto.setStatusStatistics(statusStatistics);
        return blnoStatisticsRespDto;
    }

    private void saveOperateLogBatch(List<Long> ids, String operateType, String operateTypeSub, String operateDesc,
                                     String remark) {
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>().in(BlnoDO::getId, ids).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        Map<Long, String> stringMap = blnoDos.stream().collect(Collectors.toMap(BlnoDO::getId, BlnoDO::getBookingNo));
        List<OperateLogDO> operateLogDos = new ArrayList<>();
        ids.forEach(n -> {
            OperateLogDO operateLog = new OperateLogDO();
            operateLog.setBusinessId(n);
            operateLog.setBusinessNo(stringMap.get(n));
            operateLog.setBusinessType(BusinessTypeEnum.SI.getValue());
            operateLog.setOperateType(operateType);
            operateLog.setOperateTypeSub(operateTypeSub);
            operateLog.setOperateDesc(operateDesc);
            operateLog.setRemark(remark);
            operateLogDos.add(operateLog);
        });
        //todo 在operateLogService中封装统一的记录日志方法
        operateLogMapper.insertBatch(operateLogDos);
    }

    private List<BlnoDO> volatileStatus(BlnoOperateReqVO blnoOperateReqVO) {
        List<Long> ids = blnoOperateReqVO.getBlnoIds();
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>().in(BlnoDO::getId, ids).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        blnoDos.stream().anyMatch(n -> {
            if (StringUtils.isNotEmpty(blnoOperateReqVO.getSignType())) {
                if (!SiStatusEnum.BookingConfirmed.getValue().equals(n.getSiStatus())) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NOT_BOOKINGCONFIRMED);
                }
                if (!n.getBlLockFlag()) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NOT_LOCKED);
                }
                if (StringUtils.isEmpty(n.getIssuePlace())) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SIGN_TYPE_NOT_UPDATE);
                }
                if(ObjectUtil.isNotNull(n.getRestrictionFlag()) && n.getRestrictionFlag() == true){
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_KOU_DAN);
                }
                // 不存在SI修改申请审批中记录
                if (SI_AMENDMENT_PROCESSING.getValue().equals(n.getSiChangeStatus())) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_ERROR_NOTICE_EXISTED);
                }
                // 不存在SI 拆并单申请审批中记录
                if(NORMAL_SPLIT.getValue().equals(n.getSplitCombineType()) || NORMAL_COMBINE.getValue().equals(n.getSplitCombineType()) ||
                        COMBINE_NORMAL.getValue().equals(n.getSplitCombineType())){
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_EXIST_CHANGE);
                }
            }
            if (SiStatusEnum.BookingDraft.getValue().equals(n.getSiStatus())
                    || SiStatusEnum.BookingProcessing.getValue().equals(n.getSiStatus())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SI_STATUS_ILLEGAL);
            }
            if (NORMAL_SPLIT.getValue().equals(n.getSplitCombineType())
                    || NORMAL_COMBINE.getValue().equals(n.getSplitCombineType())
                    || COMBINE_NORMAL.getValue().equals(n.getSplitCombineType())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_SC_NOT_CHANGE);
            }
            return false;
        });
        return blnoDos ;
    }

    private void checkUserLimit(String oldUserId, String currentUserId) {
        if (!Objects.equals(currentUserId, oldUserId)) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_UPDATE_LIMIT);
        }
    }

    private void checkSiInfo(BlnoInfoVO blnoInfoVO) {
        BlnoBaseInfoVO blnoBasInfoVO = blnoInfoVO.getBlnoBasInfoVO();
        List<BlnoScnUpdateReqVO> blnoScns = blnoInfoVO.getBlnoScns();
        if (StringUtils.equals(blnoBasInfoVO.getPodCode(), IndiaPortConstants.INMAA)
                || IndiaPortConstants.INVTZ.equals(blnoBasInfoVO.getPodCode())) {
            blnoScns.stream().anyMatch(m -> {
                boolean check = StringUtils.isEmpty(m.getCompanyAddress()) || (StringUtils.isEmpty(m.getEmail())
                        && (StringUtils.equals(m.getPartyTypeCode(), PartyTypeCodeEnum.SHIPPER.getValue())
                        || StringUtils.equals(m.getPartyTypeCode(), PartyTypeCodeEnum.CONSIGNEE.getValue())));
                if (check) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_INDINA_SCN_ASSERT);
                }
                return false;
            });
        }
        if (!SiValidEnum.PENDING.getValue().equals(blnoBasInfoVO.getValid())) {
            if (blnoBasInfoVO.getId() == null && blnoMapper
                    .selectCount(new LambdaQueryWrapperX<BlnoDO>().eq(BlnoDO::getBlNo, blnoBasInfoVO.getBlNo()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())) != 0L) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_BLNO_REPEAT);
            }
            if (blnoBasInfoVO.getId() == null && blnoMapper
                    .selectCount(new LambdaQueryWrapperX<BlnoDO>().eq(BlnoDO::getBlNo, blnoBasInfoVO.getBookingNo()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())) != 0L) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_BLNO_BOOKING_NO_REPEAT);
            }
        }
        this.checkCntrSiInSo(CollectionUtil.isEmpty(blnoInfoVO.getBookingNos())
                ? Arrays.asList(blnoBasInfoVO.getBookingNo()) : blnoInfoVO.getBookingNos(), blnoInfoVO.getBlnoCntrs());
        if (CollectionUtil.isNotEmpty(blnoInfoVO.getBlnoPayments())) {
            List<BlnoPaymentUpdateReqVO> blnoPayments = blnoInfoVO.getBlnoPayments();
            Set<String> chargeCodeList =
                    blnoPayments.stream().map(BlnoPaymentUpdateReqVO::getChargeCode).collect(Collectors.toSet());
            if (chargeCodeList.size() != blnoPayments.size()) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CAN_CHARGE_CODE_REPEAT);
            }
        }
        checkVslvoy(blnoInfoVO.getBlnoVslvoys());
        checkScn(blnoInfoVO.getBlnoScns());
        checkNotify(blnoInfoVO.getBlnoScns(), blnoBasInfoVO.getPodCode());
    }

    private void checkNotify(List<BlnoScnUpdateReqVO> blnoScns, String podCode) {
        if (podCode.startsWith(INDIA_PREFEX)) {
            blnoScns.stream().filter(n -> PartyTypeCodeEnum.NOTIFY.getValue().equals(n.getPartyTypeCode()) &&
                    !BlnoScnSpecialCompanyConstants.SAME_SACONSIGNEE.equals(n.getCompanyName())).forEach(n -> {
                        if (StringUtils.isEmpty(n.getEmail())) {
                            throw ServiceExceptionUtil.exception(BlnoScnErrorCodeConstants.BLNO_INDIA_NOTIFY_EMAIL_NOT_EXISTS);
                        }
                    }
            );
        }
    }

    private void checkScn(List<BlnoScnUpdateReqVO> blnoScns) {
        if (CollectionUtil.isNotEmpty(blnoScns)) {
            for (BlnoScnUpdateReqVO n : blnoScns) {
                Boolean flag = (PartyTypeCodeEnum.CONSIGNEE.getValue().equals(n.getPartyTypeCode()) && (n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_ORDER)
                        || n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_THE_ORDER))) ||
                        ((PartyTypeCodeEnum.NOTIFY.getValue().equals(n.getPartyTypeCode()) || PartyTypeCodeEnum.NOTIFY2.getValue().equals(n.getPartyTypeCode()))
                                && n.getCompanyName().equals(BlnoScnSpecialCompanyConstants.SAME_SACONSIGNEE));
                if (flag) {
                    continue;
                }
                if (StringUtils.isEmpty(n.getUniqueIdentifierPre())) {
                    throw ServiceExceptionUtil.exception(BlnoScnErrorCodeConstants.BLNO_SCN_UNIQUEIDENTIFIERPRE_NOT_EXISTS);
                }
                if (StringUtils.isEmpty(n.getUniqueIdentifierFix())) {
                    throw ServiceExceptionUtil.exception(BlnoScnErrorCodeConstants.BLNO_SCN_UNIQUEIDENTISUFFIX_NOT_EXISTS);
                }
                if (StringUtils.isEmpty(n.getCountryCode())) {
                    throw ServiceExceptionUtil.exception(BlnoScnErrorCodeConstants.BLNO_SCN_COUNTRYCODE_NOT_EXISTS);
                }
                if (ObjectUtil.isNull(n.getAddressPrintFlag())) {
                    n.setAddressPrintFlag(false);
                }
                if (StringUtils.isEmpty(n.getCompanyAddress())) {
                    throw ServiceExceptionUtil.exception(BlnoScnErrorCodeConstants.BLNO_SCN_COMPANYADDRESS_NOT_EXISTS);
                }
            }
            blnoScns.stream().filter(n -> PartyTypeCodeEnum.CONSIGNEE.getValue().equals(n.getPartyTypeCode()) &&
                    (!n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_ORDER)) && !n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_THE_ORDER)).forEach(n -> {
                if (StringUtils.isEmpty(n.getEmail())) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SCN_NOT_CONSIGNEE);
                }
            });
            blnoScns.stream().filter(n -> PartyTypeCodeEnum.CONSIGNEE.getValue().equals(n.getPartyTypeCode()) &&
                    (n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_ORDER)) || n.getCompanyName().startsWith(BlnoScnSpecialCompanyConstants.TO_THE_ORDER)).forEach(n -> {
                if (blnoScns.stream().anyMatch(m -> PartyTypeCodeEnum.NOTIFY.getValue().equals(m.getPartyTypeCode()) &&
                        StringUtils.isEmpty(m.getEmail()))) {
                    throw ServiceExceptionUtil.exception(BlnoScnErrorCodeConstants.BLNO_SCN_NOTIFY_EMAIL_NOT_EXISTS);
                }
            });
        }
    }

    private void checkVslvoy(List<BlnoVslvoyUpdateReqVO> siVslvoyList) {
        if (CollectionUtil.isNotEmpty(siVslvoyList)) {
            siVslvoyList.forEach(e -> {
                if (TransmodeEnum.LINER.equals(e.getTransmode())) {
                    VoyageLockDO voyageLock = voyageLockService.findVoyageLock(e.getVesselCode(), e.getVoyageEx(), e.getPolCode(), ExImTypeEnum.EX.getValue());
                    if (voyageLock != null && voyageLock.getVoyageLockFlag()) {
                        throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_VOYAGE_LOCK_ERROR);
                    }
                }
            });
        }
    }

    public void checkCntrSiInSo(List<String> bookingNos, List<BlnoCntrUpdateReqVO> blnoCntrs) {
        List<SoDO> soDos = soMapper.selectList(new LambdaQueryWrapperX<SoDO>()
                .eq(SoDO::getValidStatus, 1)
                .in(SoDO::getBookingNo, bookingNos)
        );
        if (CollectionUtil.isEmpty(soDos)) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_NOT_EXISTS);
        }
        List<Long> soIds = soDos.stream().map(SoDO::getId).collect(Collectors.toList());
        List<SoForecastDO> soForecastDos = soForecastMapper.selectList(new LambdaQueryWrapper<SoForecastDO>().in(SoForecastDO::getSoId, soIds));
        if (CollectionUtil.isNotEmpty(soForecastDos) && CollectionUtil.isNotEmpty(blnoCntrs)) {
            Map<String, Long> cntrProperties = blnoCntrs.stream()
                    .collect(Collectors.groupingBy(
                            n -> n.getCntrType() + "-" + n.getCntrSize(),
                            Collectors.counting()
                    ));
            List<String> soCntrInfo = soForecastDos.stream().map(n -> n.getCntrType() + "-" + n.getCntrSize()).distinct().collect(Collectors.toList());
            Map<String, Long> soCntrMap = soForecastDos.stream().collect(Collectors.toMap(n -> n.getCntrType() + "-" + n.getCntrSize()
                    , SoForecastDO::getCntrQuantity, (existing, replacement) -> existing));
            for (String key : cntrProperties.keySet()) {
                if (!soCntrInfo.contains(key)) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SO_CNTR_NOT_EQUAL);
                }
                Long siNum = cntrProperties.get(key);
                Long soNum = soCntrMap.get(key);
                if (siNum.compareTo(soNum) > 0) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_LESS_THEN_SO);
                }
            }

        }
    }

    public void checkCargoSiInSo(String bookingNo, List<BlnoCargoUpdateReqVO> blnoCargos) {
        List<ManifestDO> manifestDos = manifestMapper.selectList(new LambdaQueryWrapperX<ManifestDO>()
                .eq(ManifestDO::getBookingNo, bookingNo)
                .eq(ManifestDO::getMasterMfNo, null)
        );
        if (CollectionUtil.isNotEmpty(manifestDos)) {
            ManifestDO manifestDO = manifestDos.get(0);
            List<ManifestCargoDO> manifestCargoDos = manifestCargoMapper.selectList(new LambdaQueryWrapper<ManifestCargoDO>().eq(ManifestCargoDO::getManifestId, manifestDO.getId()));
            if (CollectionUtil.isNotEmpty(manifestCargoDos)) {
                BlnoCargoUpdateReqVO blnoCargoUpdateReqVO = blnoCargos.get(0);
                if (blnoCargoUpdateReqVO.getQuantity()
                        .compareTo(manifestCargoDos.stream().mapToInt(ManifestCargoDO::getQuantity).sum()) != 0) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_MANIFEST_NOT_QUANTITY);
                }
                if (blnoCargoUpdateReqVO.getGrossWeight().compareTo(manifestCargoDos.stream()
                        .map(ManifestCargoDO::getGrossWeight).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_MANIFEST_NOT_WEIGHT);
                }
                if (blnoCargoUpdateReqVO.getMeasurement().compareTo(manifestCargoDos.stream()
                        .map(ManifestCargoDO::getMeasurement).reduce(BigDecimal.ZERO, BigDecimal::add)) != 0) {
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_MANIFEST_NOT_MEASUREMENT);
                }
            }
        }
    }

    /**
     * 签收类型更新
     *
     * @param updateDto updateDto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSignType(BlnoSignTypeUpdateDto updateDto) {
        BlnoOperateReqVO blnoOperateReqVO = new BlnoOperateReqVO();
        blnoOperateReqVO.setBlnoIds(updateDto.getBlnoIds());
        blnoOperateReqVO.setSignType(updateDto.getSignType());
        volatileStatus(blnoOperateReqVO);

        if (updateDto.getSignType() != null) {
            if (!CommonConstants.SIGN_TYPE_LIST.contains(updateDto.getSignType())) {
                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SIGN_TYPE_ILLEGAL);
            }
        }
        LambdaUpdateWrapper<BlnoDO> lambdaUpdate = new LambdaUpdateWrapper<>();
        lambdaUpdate
                .in(BlnoDO::getId, blnoOperateReqVO.getBlnoIds())
                .eq(BlnoDO::getDatasource, updateDto.getDataSource())
                .set(BlnoDO::getSignType, blnoOperateReqVO.getSignType())
                .set(BlnoDO::getSiStatus, BookingRejected.getValue())
                .set(BlnoDO::getIssueDate, LocalDate.parse(DateUtil.formatDate(DateUtil.date())))
                .set(BlnoDO::getUpdater, updateDto.getCreator())
        ;
        blnoMapper.update(null, lambdaUpdate);
        saveOperateLogBatch(blnoOperateReqVO.getBlnoIds(), SI_OPERATE_TYPE, OPERATE_SI_THREE.getValue(),
                OPERATE_SI_THREE.getLabel(), blnoOperateReqVO.getRemark());
    }

    private void checkSiCommitInfo(BlnoInfoVO blnoInfoVO) {
        if (CollectionUtil.isEmpty(blnoInfoVO.getBlnoPayments())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_PAYMENT_NOT_EXISTED);
        }
        // 指定支付方式中是否包含海运费
        boolean flag = blnoInfoVO.getBlnoPayments().stream()
                .noneMatch(entity -> StringUtils.equals(entity.getChargeCode(), SiPaymentConstants.CHARGE_CODE_FRT));
        if (flag) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_FRT_NOT_EXISTED);
        }
        if (CollectionUtil.isEmpty(blnoInfoVO.getBlnoScns())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SCN_NOT_EXISTED);
        }
        if (CollectionUtil.isEmpty(blnoInfoVO.getBlnoCargos())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CARGO_NOT_EXISTED);
        }
        if (CollectionUtil.isEmpty(blnoInfoVO.getBlnoCntrs())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_CNTR_NOT_EXISTED);
        }
        // 只有第一段运输模式为barge的提单才能签发小船
        checkXiaoChuan(blnoInfoVO.getBlnoBasInfoVO().getIssueFeederBl(), blnoInfoVO.getBlnoVslvoys());
        checkScn(blnoInfoVO.getBlnoScns());
        //预配箱信息校验
        this.checkCntrSiInSo(CollectionUtil.isEmpty(blnoInfoVO.getBookingNos())
                ? Arrays.asList(blnoInfoVO.getBlnoBasInfoVO().getBookingNo()) : blnoInfoVO.getBookingNos(), blnoInfoVO.getBlnoCntrs());
        //预配舱单 货物 件重尺寸校验
        this.checkCargoSiInSo(blnoInfoVO.getBlnoBasInfoVO().getBookingNo(), blnoInfoVO.getBlnoCargos());
        BlnoCargoUpdateReqVO blnoCargoUpdateReqVO = blnoInfoVO.getBlnoCargos().get(0);
        if (ObjectUtil.isNull(blnoCargoUpdateReqVO.getQuantity())
                || ObjectUtil.isNull(blnoCargoUpdateReqVO.getMeasurement())
                || ObjectUtil.isNull(blnoCargoUpdateReqVO.getGrossWeight())) {
            throw ServiceExceptionUtil.exception(BlnoCargoErrorCodeConstants.BLNO_CARGO_QUANTITY_NOT_EXISTS);
        }
        if (blnoInfoVO.getBlnoCntrs().stream().mapToInt(BlnoCntrUpdateReqVO::getQuantity).sum() != blnoCargoUpdateReqVO
                .getQuantity()) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_QUANTITY_NOT_EQUAL);
        }
        BigDecimal totalCntrWeight = blnoInfoVO.getBlnoCntrs().stream().map(BlnoCntrUpdateReqVO::getGrossWeight)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (blnoCargoUpdateReqVO.getGrossWeight().compareTo(totalCntrWeight) != 0) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_WEIGHT_NOT_EQUAL);
        }
        BigDecimal totalCntrMeasurement = blnoInfoVO.getBlnoCntrs().stream().map(BlnoCntrUpdateReqVO::getMeasurement)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (blnoCargoUpdateReqVO.getMeasurement().compareTo(totalCntrMeasurement) != 0) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_MEASUREMENT_NOT_EQUAL);
        }
    }

    private void checkXiaoChuan(Boolean issueFeederBl, List<BlnoVslvoyUpdateReqVO> blnoVslvoys) {
        if (issueFeederBl) {
            blnoVslvoys.stream().filter(n -> n.getRouteNo() == 1).forEach(n -> {
                        if (!n.getTransmode().equals(BlnoConstants.BARGE)) {
                            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_TRANSMODEL_NOT_BARGE);
                        }
                    }
            );
        }
    }

    private BlnoInfoVO getDetail(Long id) {
        BlnoInfoVO detail = new BlnoInfoVO();
        BlnoDO blnoDO = blnoMapper.selectById(id);
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        detail.setBlnoBasInfoVO(BeanUtil.copyProperties(blnoDO, BlnoBaseInfoVO.class));
        detail.setBlnoVslvoys(
                BeanUtil.copyToList(blnoVslvoyService.getBlnoVslvoyBySiId(id), BlnoVslvoyUpdateReqVO.class));
        this.setVslvoyTerminal(detail.getBlnoVslvoys(),ExImTypeEnum.EX.getValue());
        vslvoyUtil.setEtaAndEtd(detail.getBlnoVslvoys());
        detail.setBlnoPayments(
                BeanUtil.copyToList(blnoPaymentService.getBlnoPaymentsByBlnoId(id), BlnoPaymentUpdateReqVO.class));
        detail.setReqVOList(
                BeanUtil.copyToList(requirementsService.getRequirementByBlnoId(id), RequirementsUpdateReqVO.class));
        detail.setAttachmentReqVO(BeanUtil.copyToList(attachmentService.seleteByReferenceId(id, SoSiEnum.SI.getValue()),
                AttachmentUpdateReqVO.class));
        detail.setBlnoScns(BeanUtil.copyToList(blnoScnService.getBlnoScnByBlnoId(id), BlnoScnUpdateReqVO.class));
        detail
                .setBlnoCargos(BeanUtil.copyToList(blnoCargoService.getBlnoCargoByBlnoId(id), BlnoCargoUpdateReqVO.class));
        detail.setBlnoCntrs(blnoCntrService.getBlnoCntrNewByBlnoId(id, blnoDO.getBkgVesselCode(), blnoDO.getBkgVoyage()));
        detail.setBlnoFreights(getFreightList(id));
        detail.setBlnoCharges(blnoLocalChargeService.getBlnoChargeByBlnoId(id));
        detail.setBlnoCosts(blnoLocalCostExService.getBlnoCostByBlnoId(id));
        detail.setBlnoOperationLogs(
                operateLogService.selectByBusinessId(BusinessTypeEnum.SI.getValue(), id, BASIC_OPERTATE_TYPES));
        return detail;
    }

    private List<BlnoInfoVO> getDetailListByBlNos(List<String> blNos) {
        List<BlnoDO> blNoDoListByBlNo = this.getBlNoDoListByBlNo(blNos);
        if (CollectionUtil.isNotEmpty(blNoDoListByBlNo)) {
            List<Long> ids = blNoDoListByBlNo.stream().map(BlnoDO::getId).collect(Collectors.toList());
            List<BlnoVslvoyDO> blnoVslvoyBySiIdList = blnoVslvoyService.getBlnoVslvoyBySiIdList(ids);
            Map<Long, List<BlnoVslvoyDO>> vslvoyMap = Optional.ofNullable(blnoVslvoyBySiIdList).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
            List<BlnoScnDO> blnoScnByBlnoIdList = blnoScnService.getBlnoScnByBlnoIdList(ids);
            Map<Long, List<BlnoScnDO>> scnMap = Optional.ofNullable(blnoScnByBlnoIdList).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoScnDO::getBlnoId));
            List<BlnoCargoDO> blnoCargoByBlnoIds = blnoCargoService.getBlnoCargoByBlnoIds(ids);
            Map<Long, List<BlnoCargoDO>> cargoMap = Optional.ofNullable(blnoCargoByBlnoIds).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoCargoDO::getBlnoId));
            List<BlnoCntrDO> blnoCntrByBlnoIds = blnoCntrService.getBlnoCntrByBlnoIds(ids);
            Map<Long, List<BlnoCntrDO>> cntrMap = Optional.ofNullable(blnoCntrByBlnoIds).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));
            List<BlnoPaymentDO> blnoPaymentsByBlnoIds = blnoPaymentService.getBlnoPaymentsByBlnoIds(ids);
            Map<Long, List<BlnoPaymentDO>> paymentMap = Optional.ofNullable(blnoPaymentsByBlnoIds).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoPaymentDO::getBlnoId));
            List<VgmDO> vgmByBlnos = vgmService.getVgmByBlnos(blNos);
            Map<String, List<VgmDO>> vgmMap = Optional.ofNullable(vgmByBlnos).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(VgmDO::getBlNo));

            LambdaQueryWrapper<BlnoFreightDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(BlnoFreightDO::getBlnoId, ids);
            queryWrapper.orderByAsc(BlnoFreightDO::getChargeCode).orderByAsc(BlnoFreightDO::getId);
            List<BlnoFreightDO> blnoFreightByBlnoIds = blnoFreightMapper.selectList(queryWrapper);

            Map<Long, List<BlnoFreightDO>> freightMap = Optional.ofNullable(blnoFreightByBlnoIds).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoFreightDO::getBlnoId));


            ArrayList<BlnoInfoVO> blnoInfoVos = new ArrayList<>();
            List<BlnoBaseInfoVO> blnoBaseInfoVos = BeanUtil.copyToList(blNoDoListByBlNo, BlnoBaseInfoVO.class);
            blnoBaseInfoVos.stream().forEach(t -> {
                BlnoInfoVO blnoInfoVO = new BlnoInfoVO();
                blnoInfoVO.setBlnoBasInfoVO(t);
                if (CollectionUtil.isNotEmpty(vslvoyMap)) {
                    blnoInfoVO.setBlnoVslvoys(BeanUtil.copyToList(vslvoyMap.get(t.getId()), BlnoVslvoyUpdateReqVO.class));
                }
                if (CollectionUtil.isNotEmpty(scnMap)) {
                    blnoInfoVO.setBlnoScns(BeanUtil.copyToList(scnMap.get(t.getId()), BlnoScnUpdateReqVO.class));
                }
                if (CollectionUtil.isNotEmpty(cargoMap)) {
                    blnoInfoVO.setBlnoCargos(BeanUtil.copyToList(cargoMap.get(t.getId()), BlnoCargoUpdateReqVO.class));
                }
                if (CollectionUtil.isNotEmpty(cntrMap)) {
                    blnoInfoVO.setBlnoCntrs(BeanUtil.copyToList(cntrMap.get(t.getId()), BlnoCntrUpdateReqVO.class));
                }
                if (CollectionUtil.isNotEmpty(paymentMap)) {
                    blnoInfoVO.setBlnoPayments(BeanUtil.copyToList(paymentMap.get(t.getId()), BlnoPaymentUpdateReqVO.class));
                }
                if (CollectionUtil.isNotEmpty(vgmMap)) {
                    blnoInfoVO.setVgmCreateReqVos(BeanUtil.copyToList(vgmMap.get(t.getBlNo()), VgmCreateReqVO.class));
                }
                if (CollectionUtil.isNotEmpty(freightMap)) {
                    blnoInfoVO.setBlnoFreights(BeanUtil.copyToList(freightMap.get(t.getBlNo()), BlnoFreightUpdateReqVO.class));
                }

                blnoInfoVos.add(blnoInfoVO);
            });
            return blnoInfoVos;
        }
        return Collections.emptyList();
    }

    private List<BlnoFreightUpdateReqVO> getFreightList(Long blnoId) {
        LambdaQueryWrapper<BlnoFreightDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BlnoFreightDO::getBlnoId, blnoId);
        queryWrapper.orderByAsc(BlnoFreightDO::getId);
        List<BlnoFreightDO> blnoFreightDos = blnoFreightMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(blnoFreightDos)) {
            List<String> codes = blnoFreightDos.stream().map(BlnoFreightDO::getChargeCode).collect(Collectors.toList());
            List<BlnoFreightUpdateReqVO> blnoFreightUpdateReqVos =
                    BeanUtil.copyToList(blnoFreightDos, BlnoFreightUpdateReqVO.class);
            List<FinanceItemRespDTO> checkedData = null;
            try {
                FinanceItemListByCodeReqVO financeItemListByCodeReqVO = new FinanceItemListByCodeReqVO(codes);
                checkedData = financeItemApi.listByCode(financeItemListByCodeReqVO).getCheckedData();
            } catch (Exception e) {
                throw ServiceExceptionUtil.exception(BlnoLocalCostImErrorCodeConstants.BLNO_FINANCE_ERROR);
            }
            if (CollectionUtil.isNotEmpty(checkedData)) {
                Map<String, String> cnMap = checkedData.stream()
                        .collect(Collectors.toMap(FinanceItemRespDTO::getCode, FinanceItemRespDTO::getItemNameCn));
                Map<String, String> enMap = checkedData.stream()
                        .collect(Collectors.toMap(FinanceItemRespDTO::getCode, FinanceItemRespDTO::getItemNameEn));
                blnoFreightUpdateReqVos.forEach(n -> {
                    n.setChargeNameCn(cnMap.get(n.getChargeCode()));
                    n.setChargeNameEn(enMap.get(n.getChargeCode()));
                });
            }
            return blnoFreightUpdateReqVos;
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public List<BlnoTransferVO> getBlnoAll(BlnoTransferVO reqVO) {
        List<BlnoTransferVO> blnoAll = blnoMapper.getBlnoAll(reqVO);
        if (CollectionUtil.isNotEmpty(blnoAll)) {
            blnoAll.forEach(vo -> {
                vo.setTransPort(reqVO.getTransPort());
                vo.setImOrEx(reqVO.getImOrEx());
            });
        }
        return blnoAll;
    }

    @Override
    public IPage<BlnoPageRespDto> rpcPageBlnoInfo(IPage<BlnoPageRespDto> page, BlnoPageReqDto blnoPageReqDto) {
        return blnoMapper.rpcPageBlnoInfo(page, blnoPageReqDto);
    }

    @Override
    public List<BlnoCntrStaticDto> rpcBlnoCntr(BlnoPageReqDto blnoPageReqDto) {
        return blnoMapper.rpcBlnoCntr(blnoPageReqDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean draftConfirm(DraftConfirmDto draftConfirmDto) {
        BlnoDO blnoDo = blnoMapper.selectById(draftConfirmDto.getBlId());
        if (blnoDo == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        //draft确认增加判断逻辑：是否SI存在修改申请审批中记录或者存在拆并单申请审批中记录 如果存在不能进行确认Draft件
        if (NORMAL_SPLIT.getValue().equals(blnoDo.getSplitCombineType()) || NORMAL_COMBINE.getValue().equals(blnoDo.getSplitCombineType()) || COMBINE_NORMAL.getValue().equals(blnoDo.getSplitCombineType())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_SPLIT_OR_COMBINE);
        }
        if (this.isExistChangeByBlNoId(blnoDo.getId())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_IS_CHANGING);
        }
        if (!SiStatusEnum.AwaitingMaterialUploaded.getValue().equals(blnoDo.getSiStatus())) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NOT_SI04);
        }
        //判断draft打印数据是否存在
        LambdaQueryWrapperX<BlnoBlPrintLogDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(BlnoBlPrintLogDO::getBlnoId, blnoDo.getId());
        BlnoBlPrintLogDO blnoBlPrintLogDO = blnoBlPrintLogMapper.selectOne(queryWrapperX);
        if (ObjectUtils.isEmpty(blnoBlPrintLogDO) || ObjectUtils.isEmpty(blnoBlPrintLogDO.getDraftFileId()) || ObjectUtils.isEmpty(blnoBlPrintLogDO.getDraftFileName())) {
            throw ServiceExceptionUtil.exception(BlnoBlPrintLogErrorCodeConstants.BLNO_UNPRINTED_DRAFT);
        }

        blnoDo.setSiStatus(SiStatusEnum.BookingConfirmed.getValue());
        int updateResult = blnoMapper.updateById(blnoDo);

        //提单打印记录表写入确认人和确认时间
        LambdaUpdateWrapper<BlnoBlPrintLogDO> blnoBlPrintLogDoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        blnoBlPrintLogDoLambdaUpdateWrapper.eq(BlnoBlPrintLogDO::getBlnoId, draftConfirmDto.getBlId())
                .eq(BlnoBlPrintLogDO::getDeleted, false);
        blnoBlPrintLogDoLambdaUpdateWrapper.set(BlnoBlPrintLogDO::getDraftConfirmer, draftConfirmDto.getConfirmer())
                .set(BlnoBlPrintLogDO::getDraftConfirmDate, draftConfirmDto.getConfirmDate());
        int update = blnoBlPrintLogMapper.update(null, blnoBlPrintLogDoLambdaUpdateWrapper);
        log.info("draftConfirm变更成功，主表变更数量：{}，记录表变更数量：{}", updateResult, update);
        return updateResult > 0;
    }

    private boolean isExistChangeByBlNoId(Long blNoId) {
        return confirmNoticeMapper.exists(new LambdaQueryWrapperX<ConfirmNoticeDO>()
                .eq(ConfirmNoticeDO::getBlnoId, blNoId)
                .eq(ConfirmNoticeDO::getStatus, ConfirmNoticeStatusEnum.PENDING.getValue())
                .in(ConfirmNoticeDO::getNotice, Arrays.asList(SIC.getValue(), COD.getValue(), MCN.getValue())));

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchDraftConfirm(BlnoPrintBatchOperationReqDto blnoPrintBatchOperationReqDto) {
        List<Long> blIds = blnoPrintBatchOperationReqDto.getBlIds();
        blIds.forEach(blid -> {
            DraftConfirmDto draftConfirmDto = new DraftConfirmDto();
            draftConfirmDto.setBlId(blid);
            draftConfirmDto.setConfirmer(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
            draftConfirmDto.setConfirmDate(LocalDateTime.now());
            this.draftConfirm(draftConfirmDto);
        });

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> flushBlEntity(BlnoPrintBatchOperationReqDto blnoPrintBatchOperationReqDto) {
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>()
                .in(BlnoDO::getId, blnoPrintBatchOperationReqDto.getBlIds())
                .eq(BlnoDO::getPodBlFlag, true)
                .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));

        List<BlnoDO> flushEntityBlnoDos = this.filterFlushEntity(blnoDos);
        if (!ObjectUtils.isEmpty(flushEntityBlnoDos)) {
            flushEntityBlnoDos.forEach(blno -> {

                BlnoBlPrintLogDO blnoBlPrintLogDO = blnoBlPrintLogMapper.selectOne(
                        new LambdaQueryWrapper<BlnoBlPrintLogDO>().eq(BlnoBlPrintLogDO::getBlnoId, blno.getId())
                );
                //如果提单存在已打印的preview,那么需要重新生成preview文件
                boolean printedPreview = !ObjectUtils.isEmpty(blnoBlPrintLogDO)
                        && (!ObjectUtils.isEmpty(blnoBlPrintLogDO.getPreviewFileId()) || !ObjectUtils.isEmpty(blnoBlPrintLogDO.getPreviewFileName()));
                if (printedPreview) {
                    blnoBlPrintLogService.flushPrintFile(blno, PrintStatusEnum.PREVIEWBL.getValue());
                }
                //更新提单主体
                blnoBlPrintLogService.updateBlEntiyt(blno, blno.getBlEntity());
            });
            return flushEntityBlnoDos.stream().map(BlnoDO::getBlNo).distinct().collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    private List<BlnoDO> filterFlushEntity(List<BlnoDO> blnoDos) {
        List<BlnoVslvoyDO> blnoVslvoyBySiIdList = blnoVslvoyService.getBlnoVslvoyBySiIdList(blnoDos.stream().map(BlnoDO::getId).collect(Collectors.toList()));
        Map<Long, List<BlnoVslvoyDO>> vslvoyMap = Optional.ofNullable(blnoVslvoyBySiIdList).orElse(Collections.emptyList()).stream().distinct().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
        List<String> laneCodeExList = blnoVslvoyBySiIdList.stream().map(BlnoVslvoyDO::getLaneCodeEx).distinct().collect(Collectors.toList());
        if (!ObjectUtils.isEmpty(laneCodeExList)) {
            List<LaneRespVO> laneRespVos = laneService.selectLanesByLaneCodes(laneCodeExList);
            Map<String, String> laneEntityMap = laneRespVos.stream().collect(Collectors.toMap(LaneRespVO::getLaneCode, LaneRespVO::getBusinessEntity, (oldValue, newValue) -> newValue));

            ArrayList<BlnoDO> flushEntityBlnoDos = new ArrayList<>();
            blnoDos.stream().forEach(blnoDo -> {
                List<BlnoVslvoyDO> vslvoyList = vslvoyMap.get(blnoDo.getId());
                BlnoVslvoyDO blnoVslvoyDo = vslvoyList.stream().filter(vs -> blnoDo.getBkgVesselCode().equals(vs.getVesselCode()) && blnoDo.getBkgVoyage().equals(vs.getVoyageEx())).findFirst().orElse(null);
                if (!ObjectUtils.isEmpty(blnoVslvoyDo)) {
                    String businessEntity = laneEntityMap.get(blnoVslvoyDo.getLaneCodeEx());
                    if (!ObjectUtils.isEmpty(businessEntity) && !businessEntity.equals(blnoDo.getBlEntity())) {
                        //从航线查询出来的提单主体不为空，且和提单主表原提单主体不同，则需要更新提单主体
                        blnoDo.setBlEntity(businessEntity);
                        flushEntityBlnoDos.add(blnoDo);
                    }
                }
            });
            return flushEntityBlnoDos;
        }
        return null;


    }


    public List<String> getBlNoByIdListNotDependOnDeleted(List<Long> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return null;
        }
        return blnoMapper.getBlNoByIdListNotDependOnDeleted(ids);
    }

    @Override
    public Boolean assertGiveNotice(AssertNoticeVO assertNoticeVO) {
        if (StringUtils.equals(assertNoticeVO.getOldDelCode(), assertNoticeVO.getNewDelCode())) {
            return false;
        }
        ConfirmNoticeDO confirmNoticeDO = confirmNoticeMapper.selectById(assertNoticeVO.getNoticeId());
        if (confirmNoticeDO == null) {
            throw ServiceExceptionUtil.exception(ConfirmNoticeErrorCodeConstants.CONFIRM_NOTICE_NOT_EXISTS);
        }
        BlnoDO blnoDO = blnoMapper.selectById(confirmNoticeDO.getBlnoId());
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        List<BlnoCntrDO> cntrDos = blnoCntrMapper.selectList(new LambdaQueryWrapper<BlnoCntrDO>().eq(BlnoCntrDO::getBlnoId, blnoDO.getId()));
        if (CollectionUtil.isEmpty(cntrDos)) {
            return false;
        }
        List<String> containNos = cntrDos.stream().map(BlnoCntrDO::getCntrNo).collect(Collectors.toList());
        List<CntrBaseInfoDTO> checkedData;
        try {
            CommonResult<List<CntrBaseInfoDTO>> listCommonResult = cntrInfoApi.queryCntrInfo(containNos);
            checkedData = listCommonResult.getCheckedData();
        } catch (Exception e) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.EQU_REQUEST_FAILURE);
        }
        return CollectionUtil.isNotEmpty(checkedData) &&
                checkedData.stream().anyMatch(n -> StringUtils.isNotEmpty(n.getCarrier()) && n.getCarrier().startsWith(BlnoConstants.SNLOW));
    }

    @Override
    public List<SoDO> checkSoCombineSi(BlnoMultipleSoVO blnoMultipleSoVO) {
        List<Long> soIds = blnoMultipleSoVO.getSoIds();
        if (soIds.size() < 2) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_COMBINE_SI_NUM_LESS);
        }
        List<SoDO> soDos = soMapper.selectList(new LambdaQueryWrapper<SoDO>().in(SoDO::getId, soIds));
        if (CollectionUtil.isEmpty(soDos) || soDos.size() < soIds.size()) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_NUM_LESS);
        }
        if (soDos.stream().anyMatch(n -> SEND_EDO_PORT.contains(n.getPolCode()))) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_NOT_BEI_FANG_SAN_GANG);
        }
        if (soDos.stream().anyMatch(n -> CNSHA.equals(n.getPolCode()) && !CNSHA.equals(n.getPorCode()))) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_NOT_CNSHA);
        }
        if (soDos.stream().anyMatch(n -> CNNGB.equals(n.getPolCode()) && !CNNGB.equals(n.getPorCode()) &&
                configuration.getTopAgent().contains(n.getTopAgent()))) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_NOT_CNNGB);
        }
        List<String> bookingNos = soDos.stream().map(SoDO::getBookingNo).collect(Collectors.toList());
        SoDO soDO = soMapper.selectById(blnoMultipleSoVO.getSoId());
        if (ObjectUtil.isNull(soDO)) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_MAIN_ERROR);
        }
        List<BlnoDO> blnoDos = blnoMapper.selectList(new LambdaQueryWrapperX<BlnoDO>()
                .inIfPresent(BlnoDO::getBookingNo, bookingNos)
                .eq(BlnoDO::getBlNo, soDO.getBlNo())
                .eq(BlnoDO::getPodBlFlag, true)
                .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()));
        if (CollectionUtil.isNotEmpty(blnoDos)) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_SI_EXISTED);
        }
        if (soDos.stream().anyMatch(n -> !SoStatusEnum.BookingConfirmed.getValue().equals(n.getSoStatus()))) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_STATUS_ERROR);
        }
        List<SoAmendApplyDO> soAmendApplyDos = soAmendApplyMapper.selectList(new LambdaQueryWrapperX<SoAmendApplyDO>()
                .inIfPresent(SoAmendApplyDO::getBookingNo, bookingNos)
                .inIfPresent(SoAmendApplyDO::getSoAmendStatus,
                        Arrays.asList(ContentChangeStatusEnum.BOOKING_CHANGE_PROCESSING.getValue(),
                                ContentChangeStatusEnum.BOOKING_CHANGE_AWAITING_MATERIAL_UPLOADED.getValue(),
                                ContentChangeStatusEnum.BOOKING_CANCEL_PROCESSING.getValue())));
        if (CollectionUtil.isNotEmpty(soAmendApplyDos)) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_STATUS_AMEND);
        }

        List<SoCargoDO> soCargoDos = soCargoMapper.selectList(new LambdaQueryWrapper<SoCargoDO>().in(SoCargoDO::getSoId, soIds));
        if (CollectionUtil.isNotEmpty(soCargoDos)) {
            if (soCargoDos.stream().anyMatch(n -> BlnoConstants.CARGO_TYPES.contains(n.getCargoType()))) {
                throw ServiceExceptionUtil
                        .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_CARGO_TYPE_ERROR);
            }
            if (soCargoDos.stream().anyMatch(n -> BlnoConstants.DRY.equals(n.getCargoType()) && StringUtils.isNotEmpty(n.getSubCargoType()))) {
                throw ServiceExceptionUtil
                        .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_DRY_ERROR);
            }
        }

        if (soDos.stream().map(SoDO::getRouteId).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_ROUTE_SAME);
        }
        if (soDos.stream()
                .map(n -> Arrays.asList(n.getPolCode(), n.getPorCode(), n.getPodCode(), n.getDelCode()))
                .distinct()
                .count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_PORT_CODE_SAME);
        }
        if (soDos.stream()
                .map(n -> n.getBookingVessel() + n.getBookingVoyage())
                .distinct()
                .count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_VESSEL_SAME);
        }
        if (soDos.stream().map(SoDO::getBookingParty).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_BKP_CODE_SAME);
        }
        if (soDos.stream().map(SoDO::getShippingTerm).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_SHIP_SAME);
        }
        if (soDos.stream().map(SoDO::getAgreementNo).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_AGREEMENT_SAME);
        }
        if (soDos.stream().map(SoDO::getSpaceOwner).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_SPACE_OWNER_SAME);
        }
        if (soDos.stream().map(SoDO::getCntrStatus).distinct().count() > 1) {
            throw ServiceExceptionUtil
                    .exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_CNTR_STATUS_SAME);
        }
        return soDos;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createSoCombineSi(BlnoMultipleSoCreateVO blnoMultipleSoCreateVO) {
        BlnoInfoVO blnoInfoVO = blnoMultipleSoCreateVO.getBlnoInfoVO();
        BlnoMultipleSoVO blnoMultipleSoVO = blnoMultipleSoCreateVO.getBlnoMultipleSoVO();
        List<SoDO> soDos = this.checkSoCombineSi(blnoMultipleSoVO);
        blnoInfoVO.setBookingNos(soDos.stream().map(SoDO::getBookingNo).collect(Collectors.toList()));
        Long newSiId = this.createOrUpdateSi(blnoInfoVO, false, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        this.dealSoRelation(blnoMultipleSoCreateVO, soDos, newSiId);
        return newSiId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitSoCombineSi(BlnoMultipleSoCreateVO blnoMultipleSoCreateVO) {
        BlnoInfoVO blnoInfoVO = blnoMultipleSoCreateVO.getBlnoInfoVO();
        BlnoMultipleSoVO blnoMultipleSoVO = blnoMultipleSoCreateVO.getBlnoMultipleSoVO();
        List<SoDO> soDos = this.checkSoCombineSi(blnoMultipleSoVO);
        blnoInfoVO.setBookingNos(soDos.stream().map(SoDO::getBookingNo).collect(Collectors.toList()));
        Long newSiId = this.commitSi(blnoInfoVO, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        this.dealSoRelation(blnoMultipleSoCreateVO, soDos, newSiId);
        return newSiId;
    }

    @Override
    public List<SoForecastRespVO> getForecastInfo(BlnoForecastReqVO blnoForecastReqVO) {
        if (CollectionUtil.isEmpty(blnoForecastReqVO.getBookingNos()) && ObjectUtil.isNull(blnoForecastReqVO.getBlnoId())) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_BL_PARAM_ERROR);
        }
        List<String> bookingNos = blnoForecastReqVO.getBookingNos();
        if (CollectionUtil.isEmpty(bookingNos)) {
            List<BlnoMultipleSoDO> blnoMultipleSoDos = blnoMultipleSoMapper.selectList(new LambdaQueryWrapper<BlnoMultipleSoDO>().eq(BlnoMultipleSoDO::getBlnoId, blnoForecastReqVO.getBlnoId()));
            if (CollectionUtil.isNotEmpty(blnoMultipleSoDos)) {
                bookingNos = blnoMultipleSoDos.stream().map(BlnoMultipleSoDO::getBookingNo).collect(Collectors.toList());
            } else {
                bookingNos = Arrays.asList(blnoMapper.selectById(blnoForecastReqVO.getBlnoId()).getBookingNo());
            }
        }
        List<SoDO> soDos = soMapper.selectList(new LambdaQueryWrapperX<SoDO>()
                .eq(SoDO::getValidStatus, 1)
                .in(SoDO::getBookingNo, bookingNos)
        );
        if (CollectionUtil.isEmpty(soDos)) {
            throw ServiceExceptionUtil.exception(SoErrorCodeConstants.SO_NOT_EXISTS);
        }
        List<Long> soIds = soDos.stream().map(SoDO::getId).collect(Collectors.toList());
        List<SoForecastDO> soForecastDos = soForecastMapper.selectList(new LambdaQueryWrapper<SoForecastDO>().in(SoForecastDO::getSoId, soIds));
        Map<String, Long> map = soForecastDos.stream()
                .collect(Collectors.groupingBy(
                        n -> n.getCntrType() + "-" + n.getCntrSize(),
                        Collectors.summingLong(SoForecastDO::getCntrQuantity)
                ));
        List<SoForecastDO> distinctSoForecastDos = soForecastDos.stream()
                .collect(Collectors.toMap(
                        // 拼接key
                        p -> p.getCntrType() + "_" + p.getCntrSize(),
                        Function.identity(),
                        // 保留第一个出现的
                        (existing, replacement) -> existing
                ))
                .values()
                .stream()
                .collect(Collectors.toList());
        distinctSoForecastDos.forEach(n -> n.setCntrQuantity(map.get(n.getCntrType() + "-" + n.getCntrSize())));
        return BeanUtil.copyToList(distinctSoForecastDos, SoForecastRespVO.class);
    }

    @Override
    public void setShippingScheduleTime(List<BlnoVslvoyUpdateReqDto> blnoVslvoys) {
        if (CollectionUtils.isEmpty(blnoVslvoys)) {
            return;
        }
        for (BlnoVslvoyUpdateReqDto vslvoy : blnoVslvoys) {
            String vesselCode = vslvoy.getVesselCode();
            if (StringUtils.isEmpty(vesselCode) || vesselCode.equalsIgnoreCase(CommonConstants.DEFAULT_VESSEL_CODE)) {
                continue;
            }
            List<String> vesselCodes = new ArrayList<>();
            vesselCodes.add(vesselCode);
            VesselVoyRespDTO voyExResp = new VesselVoyRespDTO();
            VesselVoyRespDTO voyImResp = new VesselVoyRespDTO();
            try {
                voyExResp = costChargeUtil.getVoyQueryRespDTO(vslvoy.getPolCode(), vslvoy.getVoyageIm(),
                        vslvoy.getLaneCodeEx(), vesselCodes);
            } catch (Exception e) {
                log.warn("get ex voy schedule exception", e);
            }

            try {
                voyImResp = costChargeUtil.getVoyQueryRespDTO(vslvoy.getPodCode(), vslvoy.getVoyageEx(),
                        vslvoy.getLaneCodeIm(), vesselCodes);
            } catch (Exception e) {
                log.warn("get ex voy schedule exception", e);
            }
            String polTime = voyExResp.getAtd();
            if (StringUtils.isEmpty(polTime)) {
                polTime = voyExResp.getEtd();
            }
            if (StringUtils.isEmpty(polTime)) {
                polTime = voyExResp.getPtd();
            }

            String podTime = voyImResp.getAta();
            if (StringUtils.isEmpty(podTime)) {
                podTime = voyImResp.getEta();
            }
            if (StringUtils.isEmpty(podTime)) {
                podTime = voyImResp.getPta();
            }
            vslvoy.setPolEtd(polTime);
            vslvoy.setPodEta(podTime);
        }
    }

    @Override
    public List<BlnoCntrCntaoVO> selectCntaoCntr(BlnoCntrCntaoReqVO blnoCntrCntaoReqVO) {
        List<BlnoCntrCntaoVO> blnoCntrCntaoVos = blnoMapper.selectCntaoCntr(blnoCntrCntaoReqVO);
        if (CollectionUtil.isNotEmpty(blnoCntrCntaoVos)) {
            nameTransferService.fillName(blnoCntrCntaoVos);
        }
        return blnoCntrCntaoVos;
    }

    @Override
    public BlnoCntrCntaoCompareRespVO checkCntaoCntr(BlnoCntrCntaoReqVO blnoCntrCntaoReqVO) {
        BlnoCntrCntaoCompareRespVO blnoCntrCntaoCompareRespVO = new BlnoCntrCntaoCompareRespVO();
        CntaoCntrImportRespVO cntaoCntrImportRespVO = new CntaoCntrImportRespVO();
        List<CntaoCntrImportVO> importVos = null;
        try {
            importVos = EasyExcel.read(blnoCntrCntaoReqVO.getFile().getInputStream(), CntaoCntrImportVO.class, null)
                    .autoCloseStream(true).doReadAllSync();
        } catch (IOException e) {
            e.printStackTrace();
        }
        cntaoCntrImportRespVO.setCntaoCntrImportVos(importVos);
        Map<String, Long> map = importVos.stream()
                .collect(Collectors.groupingBy(
                        p -> p.getPodCode() + "-" + p.getCntrType() + "-" + p.getCntrSize(),
                        Collectors.counting()
                ));
        List<CntaoCntrImportCountVO> cntaoCntrImportCountVos = new ArrayList<>();
        for (String key : map.keySet()){
            CntaoCntrImportCountVO cntaoCntrImportCountVO = new CntaoCntrImportCountVO();
            String[] split = key.split("-");
            cntaoCntrImportCountVO.setPodCode(split[0]);
            cntaoCntrImportCountVO.setCntrType(split[1]);
            cntaoCntrImportCountVO.setCntrSize(split[2]);
            cntaoCntrImportCountVO.setNum(map.get(key).intValue());
            cntaoCntrImportCountVos.add(cntaoCntrImportCountVO);
        }
        cntaoCntrImportRespVO.setCntaoCntrImportCountVos(cntaoCntrImportCountVos);
        blnoCntrCntaoCompareRespVO.setCntaoCntrImportRespVO(cntaoCntrImportRespVO);

        List<BlnoCntrCntaoVO> blnoCntrCntaoVos = this.selectCntaoCntr(blnoCntrCntaoReqVO);
        blnoCntrCntaoCompareRespVO.setInnerCntrVos(blnoCntrCntaoVos);

        List<BlnoCntrCntaoCompareVO> blnoCntrCntaoCompareVos = dealCheckResult(importVos,blnoCntrCntaoVos);
        blnoCntrCntaoCompareRespVO.setBlnoCntrCntaoCompareVos(blnoCntrCntaoCompareVos);

        return blnoCntrCntaoCompareRespVO;
    }

    @Override
    public void saveSuspectedDg(Map<String, String> map) {
        List<SuspectedDgReqVO> suspectedDgReqVos = new ArrayList<>();
        for (String key : map.keySet()){
            Map<String, Object> paramMap = MapUtil.newHashMap(16);
            paramMap.put("text",map.get(key));
            paramMap.put("top_num","1");
            CommodityEnAiCheckResp commodityEnAiCheckResp = HttpClientUtil.sendPost(configuration.getCommodityEnAICheckPostUrl(), paramMap, null, CommodityEnAiCheckResp.class);
            if(CollectionUtil.isNotEmpty(commodityEnAiCheckResp.getResults())) {
                CommodityEnAiCheckResp.Demo demo = commodityEnAiCheckResp.getResults().get(0);
                SuspectedDgReqVO suspectedDgReqVO = new SuspectedDgReqVO();
                suspectedDgReqVO.setBlno(key);
                suspectedDgReqVO.setAiResult(demo.getName());
                suspectedDgReqVO.setAiScore(demo.getScore());
                suspectedDgReqVos.add(suspectedDgReqVO);
            }
        }
        specialCargoDgService.saveSuspectedDg(suspectedDgReqVos);
    }

    @Override
    public List<SoForecastBaseVO> getSoForecastBySoId(Long siId,String blNo){
        //多so的预配箱信息
        List<Long> soIds = new ArrayList<>();
        // 查询si是否有合并
        List<BlnoMultipleSoDO> blnoMultipleSoList = blnoMultipleSoMapper.selectList(BlnoMultipleSoDO::getBlnoId,siId);
        if(org.apache.commons.collections.CollectionUtils.isNotEmpty(blnoMultipleSoList)){
            soIds.addAll(blnoMultipleSoList.stream().map(BlnoMultipleSoDO::getSoId).collect(Collectors.toList()));
        }
        List<String> blNos = new ArrayList<>(Collections.singleton(blNo));

        // 查询合并的数据
        List<BlnoSpiltCombineDetailDO> blnoSpiltCombineList = blnoSpiltCombineDetailMapper.selectList(new LambdaQueryWrapperX<BlnoSpiltCombineDetailDO>().eq(BlnoSpiltCombineDetailDO::getNewBlnoId, siId));
        if(CollectionUtil.isNotEmpty(blnoSpiltCombineList)) {
            List<BlnoDO> blList = blnoMapper.selectList(BlnoDO::getId,blnoSpiltCombineList.stream().map(BlnoSpiltCombineDetailDO::getBlnoId));
            blNos.addAll(blList.stream().map(BlnoDO::getBlNo).collect(Collectors.toList()));
        }
        // 查询对应soids
        List<SoDO> soList = soMapper.selectList(new LambdaQueryWrapperX<SoDO>().in(SoDO::getBlNo, blNos).eq(SoDO::getValidStatus,ValidStatusEnum.DEFAULT_VALID.getValue()));
        if(CollectionUtil.isNotEmpty(soList)){
            soIds.addAll(soList.stream().map(SoDO::getId).collect(Collectors.toList()));
        }
        return soSplitCombineService.getSoForecast(soIds.stream().distinct().collect(Collectors.toList()));
    }

    private List<BlnoCntrCntaoCompareVO> dealCheckResult(List<CntaoCntrImportVO> importVos, List<BlnoCntrCntaoVO> blnoCntrCntaoVos) {
        List<BlnoCntrCntaoCompareVO> blnoCntrCntaoCompareVos = new ArrayList<>();
        Set<String> containImpotNos = importVos.stream().map(CntaoCntrImportVO::getCntrNo).collect(Collectors.toSet());
        Set<String> containNos = blnoCntrCntaoVos.stream().map(BlnoCntrCntaoVO::getCntrNo).collect(Collectors.toSet());
        Map<String, BlnoCntrCntaoVO> cntrCntaoVoMap = blnoCntrCntaoVos.stream().collect(Collectors.toMap(BlnoCntrCntaoVO::getCntrNo, e -> e, (o1, o2) -> o1));

        blnoCntrCntaoVos.stream().filter(n-> !containImpotNos.contains(n.getCntrNo())).forEach(n->{
            BlnoCntrCntaoCompareVO blnoCntrCntaoCompareVO = new BlnoCntrCntaoCompareVO().builder()
                    .differType(CntaoCntrCompareTypeEnum.XIANG_YUN_HAVE.getValue())
                    .innerCntrNo(n.getCntrNo())
                    .innerCntrSize(n.getCntrSize())
                    .innerCntrType(n.getCntrType())
                    .innerPodCode(n.getPodCode()).build();
            blnoCntrCntaoCompareVos.add(blnoCntrCntaoCompareVO);
        });

        importVos.stream().filter(n-> !containNos.contains(n.getCntrNo())).forEach(n->{
            BlnoCntrCntaoCompareVO blnoCntrCntaoCompareVO = new BlnoCntrCntaoCompareVO().builder()
                    .differType(CntaoCntrCompareTypeEnum.MA_TOU_HAVE.getValue())
                    .importCntrNo(n.getCntrNo())
                    .importCntrSize(n.getCntrSize())
                    .importCntrType(n.getCntrType())
                    .importPodCode(n.getPodCode()).build();
            blnoCntrCntaoCompareVos.add(blnoCntrCntaoCompareVO);
        });

        importVos.stream().filter(n-> containNos.contains(n.getCntrNo())).forEach(n->{

            BlnoCntrCntaoVO blnoCntrCntaoVO = cntrCntaoVoMap.get(n.getCntrNo());
            if(!StringUtils.equals(n.getPodCode(),blnoCntrCntaoVO.getPodCode())){
                blnoCntrCntaoCompareVos.add(generateBlnoCntrCntaoCompareVO(POD_DIFFERENT.getValue(),n,blnoCntrCntaoVO)) ;
            }
            if(!StringUtils.equals(n.getCntrType(),blnoCntrCntaoVO.getCntrType()) || !StringUtils.equals(n.getCntrSize(),blnoCntrCntaoVO.getCntrSize())){
                blnoCntrCntaoCompareVos.add(generateBlnoCntrCntaoCompareVO(CNTR_TYPE_SIZE_DIFFERENT.getValue(),n,blnoCntrCntaoVO)) ;
            }
        });
        return blnoCntrCntaoCompareVos ;
    }

    private BlnoCntrCntaoCompareVO generateBlnoCntrCntaoCompareVO(String differentType,CntaoCntrImportVO cntaoCntrImportVO,BlnoCntrCntaoVO blnoCntrCntaoVO){
        return new BlnoCntrCntaoCompareVO().builder()
                .differType(differentType)
                .importCntrNo(cntaoCntrImportVO.getCntrNo())
                .importCntrSize(cntaoCntrImportVO.getCntrSize())
                .importCntrType(cntaoCntrImportVO.getCntrType())
                .importPodCode(cntaoCntrImportVO.getPodCode())
                .innerCntrNo(blnoCntrCntaoVO.getCntrNo())
                .innerCntrType(blnoCntrCntaoVO.getCntrType())
                .innerCntrSize(blnoCntrCntaoVO.getCntrSize())
                .innerPodCode(blnoCntrCntaoVO.getPodCode())
                .build();
    }

    private void dealSoRelation(BlnoMultipleSoCreateVO blnoMultipleSoCreateVO, List<SoDO> soDos, Long newSiId) {
        BlnoInfoVO blnoInfoVO = blnoMultipleSoCreateVO.getBlnoInfoVO();
        BlnoMultipleSoVO blnoMultipleSoVO = blnoMultipleSoCreateVO.getBlnoMultipleSoVO();
        List<Long> soIds = blnoMultipleSoVO.getSoIds();
        Long soId = blnoMultipleSoVO.getSoId();
        SoDO soDO = soMapper.selectById(soId);
        if (ObjectUtil.isNull(soDO)) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_LIST_MAIN_ERROR);
        }
        if (!StringUtils.equals(soDO.getBlNo(), blnoInfoVO.getBlnoBasInfoVO().getBlNo())) {
            throw ServiceExceptionUtil.exception(BlnoSoCombineSiErrorCodeConstants.SO_BL_NO_ERROR);
        }
        LambdaUpdateWrapper<SoDO> soDoLambdaUpdateWrapper = new LambdaUpdateWrapper<SoDO>()
                .in(SoDO::getId, soIds)
                .set(SoDO::getBlNo, soDO.getBlNo());
        soMapper.update(null, soDoLambdaUpdateWrapper);
        blnoMultipleSoService.createSoSiRelation(newSiId, soDos, blnoMultipleSoVO.getSoId());
    }


}
